宝哥软件园

自定义控件中事件处理的最佳实践

编辑:宝哥软件园 来源:互联网 时间:2021-09-01

在开发XAML(WPF/UWP)应用程序时,有时我们需要创建自定义控件来满足实际需求。在自定义控件中,我们通常使用一些原生控件(如按钮、文本框等)。)来帮助完成自定义控件的功能。

与用户控件不同,自定义控件不使用代码隐藏技术。相反,它通过分离用户界面和逻辑来分离它们。因此,创建自定义控件将产生两个文件,一个是Generic.xaml,其中定义了它的模板和样式;另一个是ControlName.cs,它存储自己的逻辑,如下图所示:

在这种情况下,如果您想在代码中获取模板中定义的控件,就不像在Code-Behind中那样容易,而是需要借助于OnApplyTemplate和GetTemplateChild。它们的含义如下:

在自定义控件中,此方法通常被重写。当基类调用ApplyTemplate()方法构造可视化树时,会被调用;

GetTemplateChild:获取在ControlTemplate中定义的可视化树上具有指定名称的元素;

因此,如果我们在模板中定义一个名为PART_ViewButton的按钮,我们可以这样得到它,并为它注册一个响应事件:

public override void OnAplyTemplate(){ base。OnApplyTemplate();按钮btnView=gettemplate child(' PART _ view Button ')作为按钮;if (btnView!=null) { btnView。Click=BtnView _ Click} } private void BTN view _ click(object sender,routed eventargs e){//在此处写入响应逻辑}当我们(或其他人)想要使用此控件时,将通过为其设置模板(通常是默认模板)来执行OnApplyTemplate方法。这样做似乎没有问题。然而,事实上,它可能会导致一个听起来很严重的问题:内存泄漏。

什么是内存泄漏

内存泄漏有多种类型。一般来说,是指某一类型的资源不再使用,但仍然占用内存。换句话说,它是从托管内存区域“泄露”的。如果程序中存在多次内存泄漏,会占用大量内存,最终导致内存耗尽。

在C#中,常见的内存泄漏有:

事件侦听未被删除;

非托管资源(如数据库、文件流等。)都没有被破坏;

对于以上两种情况,它们的解决方案也非常简单,即注销事件(即移除事件监控)并调用Dispose方法(如果不是,则实现IDisposable接口并销毁其中的非托管资源)。

对于第二种情况,更好理解;对于第一种情况,问题是,为什么没有删除事件监视器,这会导致内存泄漏?这是因为事件源的生命周期比事件侦听器长。看看代码:

ObjectA ObJa=new ObJect a();ObJect b ObJect=new ObJect b();objA。事件=objB。EventHanlder事件事件是在ObjectA中定义的,我们为它注册了一个事件处理程序(objB中的event handler方法)。因此,事件源objA引用了事件监听对象objB。

如果objB不再使用,我们会销毁它,但是由于objA引用了它,所以它不会被销毁或回收。在objA被摧毁之前,它不能被摧毁。因此,需要销毁的对象被其他对象引用,导致内存泄漏。

如何解决

回到自定义控件的问题,因为我们的自定义控件可能会在样式或模板上被重写,这将使OnApplyTemplate方法在这个自定义控件的生命周期中被多次执行。因此,我们需要取消注册那些由GetTemplateChild方法获取并添加了事件处理的控件的事件(例如上面代码中的btnView控件)。因为这些都是上一个模板中的控件(元素),去注册后,原来的控件和事件监听器(自定义控件本身)之间没有引用关系,避免了内存泄漏的问题。

根据我们的解决方案,前面的代码重构如下:

私有按钮btnView=nullpublic override void OnAplyTemplate(){ base。OnApplyTemplate();//取消注册事件if (btnView!=null) { btnView。点击-=BtnView _ Click;} btnView=gettemplate child(' PART _ view Button ')为Buttonif (btnView!=null) { btnView。Click=BtnView _ Click} } private void BTN view _ click(object sender,routed eventargs e){//在此写入响应逻辑},从而解决了本文开头提到的问题。但是,接下来,我们需要做一些调整。

进一步重建

试想一下,如果我们的自定义控件中有很多像btnView这样的控件,我们会在OnApplyTemplate方法中多次复制上面的代码,这样会导致OnApplyTemplate方法的复杂度增加,代码的可读性变差。

为了改进这一点,我们封装了每个控件及其事件注册和取消注册。

重构后,代码如下:

受保护的常量字符串PART _ view button=name of(PART _ view button);私有按钮btnView=null公共按钮视图按钮{ get { return btnView}设置{//取消注册事件if (btnView!=null) { btnView。点击-=BtnView _ Click;} btnView=valueif (btnView!=null) { btnView。Click=BtnView _ Click} } } public override void OnApplyTemplate(){ base。OnApplyTemplate();视图按钮=获取模板子对象(零件_视图按钮)作为按钮;} private void BTN view _ click(object sender,routed eventargs e){//在此处写入响应逻辑}对于最终代码,这里还有一些要点:

1.在OnApplyTemplate方法中,建议调用base。OnApplyTemplate()第一个;

2.是为控件注销事件还是注册事件,需要判断控件是否为空,因为用户在重写模板时有可能没有遵循TemplatePart属性中指定的控件名称;

3.将控件的名称声明为常数可以避免字符串拼写错误;

摘要

本文讨论了在WPF或UWP创建自定义控件时的内存泄漏问题。这主要是因为模板中的控制事件没有取消注册。我们不仅分析了原因,而且给出了针对这种情况的最佳实践。

虽然一般来说,这个问题不会造成很大的影响,但是如果我们注意这些细节,不仅会提高我们的代码质量和程序性能,还会在设计或处理类似问题时为我们提供必要的思路和经验。

上述XAML:自定义控件中事件处理的最佳实践方法是边肖共享的所有内容。希望能给大家一个参考,支持我们。

更多资讯
游戏推荐
更多+