• linkedu视频
  • 平面设计
  • 电脑入门
  • 操作系统
  • 办公应用
  • 电脑硬件
  • 动画设计
  • 3D设计
  • 网页设计
  • CAD设计
  • 影音处理
  • 数据库
  • 程序设计
  • 认证考试
  • 信息管理
  • 信息安全
菜单
linkedu.com
  • 网页制作
  • 数据库
  • 程序设计
  • 操作系统
  • CMS教程
  • 游戏攻略
  • 脚本语言
  • 平面设计
  • 软件教程
  • 网络安全
  • 电脑知识
  • 服务器
  • 视频教程
  • JavaScript
  • ASP.NET
  • PHP
  • 正则表达式
  • AJAX
  • JSP
  • ASP
  • Flex
  • XML
  • 编程技巧
  • Android
  • swift
  • C#教程
  • vb
  • vb.net
  • C语言
  • Java
  • Delphi
  • 易语言
  • vc/mfc
  • 嵌入式开发
  • 游戏开发
  • ios
  • 编程问答
  • 汇编语言
  • 微信小程序
  • 数据结构
  • OpenGL
  • 架构设计
  • qt
  • 微信公众号
您的位置:首页 > 程序设计 >ASP.NET > XAML: 自定义控件中事件处理的最佳实践方法

XAML: 自定义控件中事件处理的最佳实践方法

作者:WPInfo 字体:[增加 减小] 来源:互联网 时间:2018-08-20

WPInfo 通过本文主要向大家介绍了XAML,自定义控件,事件处理等相关知识,希望对您有所帮助,也希望大家支持linkedu.com www.linkedu.com

在开发 XAML(WPF/UWP) 应用程序中,有时候,我们需要创建自定义控件 (Custom Control) 来满足实际需求。而在自定义控件中,我们一般会用到一些原生的控件(如 Button、TextBox 等)来辅助以完成自定义控件的功能。

自定义控件并不像用户控件 (User Control) 一样,使用 Code-Behind(UI 与逻辑在一起)技术。相反,它通过把 UI 与逻辑分离而将两者解耦。因此,创建一个自定义控件会产生两个文件,一个是 Generic.xaml,在它里面定义其模板与样式;另一个是 <ControlName>.cs,这里面存放其逻辑,如下图:

在这种情况下,要想在代码中获取到模板里定义的控件,就不像 Code-Behind 中那么容易,而要借助于 OnApplyTemplate 和 GetTemplateChild 这两个方法。它们的意义分别如下:

OnApplyTemplate: 在自定义控件中,通常要重写这个方法,当基类调用 ApplyTemplate() 方法以构造可视化树时,会调用它;

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

所以,如果我们在模板中定义了一个名为 PART_ViewButton 的按钮,那么,我们可以这样获取它,并为它注册响应事件:

public override void OnApplyTemplate()
  {
   base.OnApplyTemplate();
   Button btnView = GetTemplateChild("PART_ViewButton") as Button;
   if (btnView != null)
   {
    btnView.Click += BtnView_Click;
   }
  }
  private void BtnView_Click(object sender, RoutedEventArgs e)
  {
   // 这里写响应逻辑
  }

当我们(或者其他人)要用这个控件时,通过给它设置了模板(一般都是默认模板)后, OnApplyTemplate 方法就会被执行。这样做看起来没什么问题。不过,其实这里有可能会引起一个听起来很严重的问题:内存泄露 (Memory Leak)。

何为内存泄露

内存泄露有多种类型,一般来说,它是指某种类型的资源不再使用,但却仍然占用内存。换句话说,它从受管理的内存区域中“泄漏”出去了。如果在程序中有多处内存泄露,将会占有很多内存,并最终导到内存被耗尽。

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

• 没有移除事件监听;

• 没有销毁非托管资源(如数据库、文件流等);

对于上面两种情况,它们的解决办法也非常简单,分别是:要反注册事件(即移除事件监听)与调用 Dispose 方法(如果没有,则要实现 IDisposable 接口,并在其中销毁非托管资源)。

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

ObjectA objA = new ObjectA(); 

ObjectB objB = new ObjectB(); 

objA.Event += objB.EventHanlder;

ObjectA 中定义了 Event 事件,我们为它注册了一个事件处理器(对象 objB 中的 EventHanlder 方法);因此,事件源 objA 对事件监听对象 objB 存在一个引用。

如果 objB 不再使用,我们要销毁它,但由于 objA 引用了它,所以它不会被销毁、回收;它要等到 objA 销毁时,才能被销毁。所以本来需要被销毁的对象,却因有其它对象对它的引用,结果造成了内存泄露。

如何解决

再回到自定义控件的问题上,因为我们的自定义控件,可能会被重写样式或者重写模板,这会使 OnApplyTemplate 方法在这个自定义控件的生命周期内被执行多次。所以,我们需要为那些通过 GetTemplateChild 方法得到并且又添加了事件处理的控件(如上述代码中的 btnView 控件)进行事件反注册。因为这些都是前一个模板中的控件(元素),当反注册后,原来的控件与事件监听者(自定义控件本身)就不存在引用关系,从而避免了内存泄露的问题。

根据我们的解决思路,对之前的代码重构如下:

private Button btnView = null;
  public override void OnApplyTemplate()
  {
   base.OnApplyTemplate();
   // 先反注册事件
   if (btnView != null)
   {
    btnView.Click -= BtnView_Click;
   }
   btnView = GetTemplateChild("PART_ViewButton") as Button;
   if (btnView != null)
   {
    btnView.Click += BtnView_Click;
   }
  }
  private void BtnView_Click(object sender, RoutedEventArgs e)
  {
   // 这里写响应逻辑
  }

这样,就解决了本文开头所说的问题。不过,接下来,我们还需要做一点调整。

进一步重构

试想,如果我们的自定义控件中,有多个类似像前述 btnView 这样的控件,我们就要将上面的代码在 OnApplyTemplate 方法中复制若干次,从而导致 OnApplyTemplate 方法的复杂度增加,以及代码的可读性变差 。

为了改善这一点,我们将每个控件以及它的事件注册与反注册封装一下。

重构后,代码如下:

protected const string PART_ViewButton = nameof(PART_ViewButton);
    private Button btnView = null;
    public Button ViewButton
    {
      get
      {
        return btnView;
      }
      set
      {
        // 先反注册事件
        if (btnView != null)
        {
          btnView.Click -= BtnView_Click;
        }
        btnView = value;
        if (btnView != null)
        {
          btnView.Click += BtnView_Click;
        }
      }
    }
    public override void OnApplyTemplate()
    {
      base.OnApplyTemplate();
      ViewButton = GetTemplateChild(PART_ViewButton) as Button;
    }
    private void BtnView_Click(object sender, RoutedEventArgs e)
    {
      // 这里写响应逻辑
    }

针对最终的代码,这里再提几点:

1. 在 OnApplyTemplate 方法中,建议一开始要先调用 base.OnApplyTemplate();

2. 无论在为控件反注册事件,还是注册事件时,都要对控件是否为空进行判断,这是因为有可能用户重写模板时没有遵循 TemplatePart 属性中所指定的控件名称;

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

总结

本文讨论了在 WPF 或 UWP 中创建自定义控件时,可能会遇到内存泄露的问题;这主要是由于模板中的控件事件没有反注册导致的。我们不仅分析了其中的原因,也给出了针对这种情况的最佳实践。

虽然在一般情况下,这一问题并不会造成较大的影响,但是,如果我们能够在这些细节上注意,这样不仅能够提高我们的代码质量与程序的性能,也能够给我们在设计或处理类似的问题时,提供必要的思路与经验。

以上这篇XAML: 自定义控件中事件处理的最佳实践方法就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持。

分享到:QQ空间新浪微博腾讯微博微信百度贴吧QQ好友复制网址打印

您可能想查找下面的文章:

  • XAML: 自定义控件中事件处理的最佳实践方法

相关文章

  • 2017-05-11Aspose.Cells 读取受保护有密码的Excel文件
  • 2017-05-11asp.net微信开发(自定义会话管理)
  • 2017-05-11关于Asp.net页面Page_Load被执行两次的问题分享
  • 2017-05-11ConfiguraionSource节点及多个配置文件的应用
  • 2017-05-11Bat自动解压缩发布asp.net程序
  • 2018-08-20ASP.NET Core 2.0 带初始参数的中间件问题及解决方法
  • 2017-05-11asp.net多文件上传实例讲解
  • 2017-05-11ASP.NET基于Ajax的Enter键提交问题分析
  • 2017-05-11读取XML并绑定至RadioButtonList实现思路及演示动画
  • 2017-05-11使用AjaxPro.Net框架实现在客户端调用服务端的方法

文章分类

  • JavaScript
  • ASP.NET
  • PHP
  • 正则表达式
  • AJAX
  • JSP
  • ASP
  • Flex
  • XML
  • 编程技巧
  • Android
  • swift
  • C#教程
  • vb
  • vb.net
  • C语言
  • Java
  • Delphi
  • 易语言
  • vc/mfc
  • 嵌入式开发
  • 游戏开发
  • ios
  • 编程问答
  • 汇编语言
  • 微信小程序
  • 数据结构
  • OpenGL
  • 架构设计
  • qt
  • 微信公众号

最近更新的内容

    • System.Web中不存在类型或命名空间名称script 找不到System.Web.Extensions.dll引用
    • 无法将函数定义与现有的声明匹配 问题的解决办法 分享
    • 如何取消.net后台线程的执行
    • 一个比较通用的分页控件,完整的设计时支持和比较流行的分页模式(提供源码下载)
    • asp.net LINQ中数据库连接字符串的问题
    • asp.net音频转换之.amr转.mp3(利用七牛转换法)
    • HttpRequest的QueryString属性 的一点认识
    • ASP.Net开发常见的一些问题总结
    • asp.net TIDFtp用法介绍
    • Asp.net(C#)实现验证码功能代码

关于我们 - 联系我们 - 免责声明 - 网站地图

©2020-2025 All Rights Reserved. linkedu.com 版权所有