【WPF.NET开发】弱事件模式

这篇具有很好参考价值的文章主要介绍了【WPF.NET开发】弱事件模式。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

本文内容

  1. 先决条件
  2. 为什么要实现弱事件模式?
  3. 应该由谁实现弱事件模式?
  4. 如何实现弱事件模式

在应用程序中,附加到事件源的处理程序可能不会与将处理程序附加到源的侦听器对象一同销毁。 这种情况下会导致内存泄漏。 Windows Presentation Foundation (WPF) 引入了可用于解决此问题的设计模式。 设计模式为特定事件提供专用的管理器类,并在该事件的侦听器上实现接口。 此设计模式称为弱事件模式。

1、先决条件

本文假定你对路由事件有基本的了解,并且已阅读
路由事件概述。 若要遵循本文中的示例,如果熟悉 Extensible Application Markup Language (XAML) 并知道如何编写 Windows Presentation Foundation (WPF) 应用程序,将会很有帮助。

2、为什么要实现弱事件模式?

对事件的侦听可能会导致内存泄漏。 侦听事件的常用技术是使用特定于语言的语法,将处理程序附加到源上的事件。 例如,C# 语句 source.SomeEvent += new SomeEventHandler(MyEventHandler) 或 VB 语句 AddHandler source.SomeEvent, AddressOf MyEventHandler。 然而,此技术可创建从事件源到事件侦听器的强引用。 除非显式注销事件处理程序,否则侦听器的对象生存期将受到源的对象生存期的影响。 在某些情况下,你可能希望通过其他因素(例如,当前是否属于应用程序的可视化树)控制侦听器的对象生存期。 每当源的对象生存期超出侦听器的有用对象生存期时,侦听器的存活时间比必要时间要长。 在这种情况下,未分配的内存相当于内存泄漏。

弱事件模式旨在解决内存泄漏问题。 当侦听器需要注册事件时,都可以使用弱事件模式,但侦听器并不明确知晓事件会在何时注销。 当源的对象生存期超过侦听器的有用对象生存期时,也可以使用弱事件模式。 在这种情况下,有用与否将由你来决定。 弱事件模式允许侦听器注册事件和接收事件,而不会以任何方式影响侦听器的对象生存期特征。 实际上,对源的隐式引用并不能确定侦听器是否有资格执行垃圾回收。 由于是弱引用,因而引用是对弱事件模式和相关 API 的命名。 侦听器可以被垃圾回收或以其他方式销毁,而源可以继续运行,无需保留针对现已销毁的对象的不可回收的处理程序引用。

3、应该由谁实现弱事件模式?

弱事件模式主要与控件作者相关。 控件作者主要负责控件行为和控件包含,以及控件对其所插入的应用程序的影响。 这包括控件对象生存期行为,特别是处理所述的内存泄漏问题。

某些方案本身就适合应用弱事件模式。 此类方案之一是数据绑定。 在数据绑定中,源对象通常独立于作为绑定目标的侦听器对象。 WPF 数据绑定的许多方面已经在事件的实现方式上应用了弱事件模式。

4、如何实现弱事件模式

有四种方法可以实现弱事件模式,每种方法都使用不同的事件管理器。 选择最适合你的方案的事件管理器。

  • 现有弱事件管理器

    当要订阅的事件具有对应的 WeakEventManager,请使用现有的弱事件管理器。 有关 WPF 附带的弱事件管理器列表,请参阅 WeakEventManager 类中的继承层次结构。 由于包含的弱事件管理器有限,可能需要选择其他方法中的一个。


  • 通用弱事件管理器:

    如果现有的 WeakEventManager<TEventSource,TEventArgs> 事件不可用,并且你正在寻找实现弱事件的最简单方法,请使用泛型 WeakEventManager。 但是,泛型 WeakEventManager<TEventSource,TEventArgs> 比现有或自定义弱事件管理器更低效,因为它使用反射从其名称中发现事件。 此外,使用泛型 WeakEventManager<TEventSource,TEventArgs> 注册事件所需的代码比使用现有或自定义 WeakEventManager 注册事件所需的代码更详细。


  • 自定义弱事件管理器:

    在现有 WeakEventManager 不可用且效率至关重要时,创建自定义的 WeakEventManager。 尽管比泛型 WeakEventManager 更有效,但自定义 WeakEventManager 要求编写更多前期代码。

  • 第三方弱事件管理器:

  • 当需要其他方法未提供的功能时,请使用第三方弱事件管理器。 NuGet 具有一些较弱的事件管理器

以下部分介绍如何通过使用不同的事件管理器类型来实现弱事件模式。 对于泛型和自定义弱事件管理器示例,要订阅的事件具有以下特征。

  • 事件名称为 SomeEvent
  • 事件由 SomeEventSource 类引发。
  • 事件处理程序的类型为 EventHandler<SomeEventArgs>
  • 事件将 SomeEventArgs 类型的参数传递给事件处理程序。

4.1 使用现有弱事件管理器类

  1. 查找现有弱事件管理器。 有关 WPF 附带的弱事件管理器列表,请参阅 WeakEventManager 类的继承层次结构。

  2. 使用新的弱事件管理器,而不是普通事件挂钩。

    例如,如果代码使用以下模式订阅事件:

    source.LostFocus += new RoutedEventHandler(Source_LostFocus);
    

    将其更改为以下模式:

    LostFocusEventManager.AddHandler(source, Source_LostFocus);
    

    同样,如果代码使用以下模式取消订阅事件:

    source.LostFocus -= new RoutedEventHandler(Source_LostFocus);
    

    将其更改为以下模式:

    LostFocusEventManager.RemoveHandler(source, Source_LostFocus);
    

4.2 使用泛型弱事件管理器类

使用泛型 WeakEventManager<TEventSource,TEventArgs> 类,而不是普通事件挂钩。

使用 WeakEventManager<TEventSource,TEventArgs> 注册事件侦听器时,需要将事件源和 EventArgs 类型作为类型参数提供给类。 调用 AddHandler,如以下代码所示:

WeakEventManager<SomeEventSource, SomeEventArgs>.AddHandler(source, "SomeEvent", Source_SomeEvent);

4.3 创建自定义弱事件管理器类

  1. 将以下类模板复制到项目。 以下类继承自 WeakEventManager 类:

    class SomeEventWeakEventManager : WeakEventManager
    {
        private SomeEventWeakEventManager()
        {
        }
    
        /// <summary>
        /// Add a handler for the given source's event.
        /// </summary>
        public static void AddHandler(SomeEventSource source,
                                      EventHandler<SomeEventArgs> handler)
        {
            if (source == null)
                throw new ArgumentNullException(nameof(source));
            if (handler == null)
                throw new ArgumentNullException(nameof(handler));
    
            CurrentManager.ProtectedAddHandler(source, handler);
        }
    
        /// <summary>
        /// Remove a handler for the given source's event.
        /// </summary>
        public static void RemoveHandler(SomeEventSource source,
                                         EventHandler<SomeEventArgs> handler)
        {
            if (source == null)
                throw new ArgumentNullException(nameof(source));
            if (handler == null)
                throw new ArgumentNullException(nameof(handler));
    
            CurrentManager.ProtectedRemoveHandler(source, handler);
        }
    
        /// <summary>
        /// Get the event manager for the current thread.
        /// </summary>
        private static SomeEventWeakEventManager CurrentManager
        {
            get
            {
                Type managerType = typeof(SomeEventWeakEventManager);
                SomeEventWeakEventManager manager =
                    (SomeEventWeakEventManager)GetCurrentManager(managerType);
    
                // at first use, create and register a new manager
                if (manager == null)
                {
                    manager = new SomeEventWeakEventManager();
                    SetCurrentManager(managerType, manager);
                }
    
                return manager;
            }
        }
    
        /// <summary>
        /// Return a new list to hold listeners to the event.
        /// </summary>
        protected override ListenerList NewListenerList()
        {
            return new ListenerList<SomeEventArgs>();
        }
    
        /// <summary>
        /// Listen to the given source for the event.
        /// </summary>
        protected override void StartListening(object source)
        {
            SomeEventSource typedSource = (SomeEventSource)source;
            typedSource.SomeEvent += new EventHandler<SomeEventArgs>(OnSomeEvent);
        }
    
        /// <summary>
        /// Stop listening to the given source for the event.
        /// </summary>
        protected override void StopListening(object source)
        {
            SomeEventSource typedSource = (SomeEventSource)source;
            typedSource.SomeEvent -= new EventHandler<SomeEventArgs>(OnSomeEvent);
        }
    
        /// <summary>
        /// Event handler for the SomeEvent event.
        /// </summary>
        void OnSomeEvent(object sender, SomeEventArgs e)
        {
            DeliverEvent(sender, e);
        }
    }
    
  2. 重命名 SomeEventWeakEventManagerSomeEventSomeEventSource 和 SomeEventArgs 以匹配事件名称。

  3. 设置弱事件管理器类的
    访问修饰符,用于匹配其管理的事件的可访问性。

  4. 使用新的弱事件管理器,而不是普通事件挂钩。

    例如,如果代码使用以下模式订阅事件:

    source.SomeEvent += new EventHandler<SomeEventArgs>(Source_SomeEvent);
    

    将其更改为以下模式:

    SomeEventWeakEventManager.AddHandler(source, Source_SomeEvent);
    

    同样,如果代码使用以下模式取消订阅事件:

    source.SomeEvent -= new EventHandler<SomeEventArgs>(Source_SomeEvent);
    

    将其更改为以下模式:文章来源地址https://www.toymoban.com/news/detail-771803.html

    SomeEventWeakEventManager.RemoveHandler(source, Source_SomeEvent);
    

到了这里,关于【WPF.NET开发】弱事件模式的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【WPF.NET开发】WPF中的文档

    文档类型 文档控件和文本布局 文档打包 XPS 文档 Windows Presentation Foundation (WPF) 提供丰富的文档功能,可创建旨在比前几代 Windows 更易于访问和读取的高保真内容文档。 除增强功能和质量外,WPF 还对文档显示、打包和安全性能提供集成服务。 本主题介绍 WPF 文档类型和文档打

    2024年01月17日
    浏览(47)
  • 【WPF.NET开发】WPF中的XAML资源

    使用 XAML 中的资源 静态和动态资源 静态资源 动态资源 样式、DataTemplate 和隐式键 资源是可以在应用中的不同位置重复使用的对象。 资源的示例包括画笔和样式。 本概述介绍如何使用 Extensible Application Markup Language (XAML) 中的资源。 你还可以使用代码创建和访问资源。  备注

    2024年01月17日
    浏览(63)
  • 【WPF.NET开发】流文档

    什么是流文档? 流文档类型 创建流内容 与流相关的类 内容架构 自定义文本 流文档旨在优化查看和可读性。 流文档根据运行时变量(例如,窗口大小、设备分辨率和可选的用户首选项)来动态调整和重新排列内容,而不是设置为一个预定义的布局。 此外,流文档还提供一

    2024年01月17日
    浏览(41)
  • 【WPF.NET开发】OpenType字体

    OpenType 字体格式 变量 大写字母 连字 花体 备用项 数字样式 版式类 本主题概述了 Windows Presentation Foundation (WPF) 中 OpenType 字体技术的一些主要功能。 OpenType 字体格式是 TrueType® 字体格式的扩展,增加了对 PostScript 字体数据的支持。 OpenType 字体格式由 Microsoft 和 Adobe Corporatio

    2024年01月16日
    浏览(42)
  • VisualStudio[WPF/.NET]基于CommunityToolkit.Mvvm架构开发

    项目模板选择如下: 暂时随机填一个目标框架,待会改: 双击“解决方案资源管理器”中项目CU-APP, 打开项目工程文件CU-APP.csproj, 修改目标框架 TargetFramework 为.net framework 4.7.2 ;  C#语言版本 LangVersion 为  9.0  \\\"工具\\\" - \\\"NuGet包管理器\\\",“管理解决方案的NuGet程序包(N)”,  或者,

    2024年02月07日
    浏览(40)
  • .NET6: 开发基于WPF的摩登三维工业软件 (7)

    Python微信订餐小程序课程视频 https://edu.csdn.net/course/detail/36074 Python实战量化交易理财系统 https://edu.csdn.net/course/detail/35475 做为一个摩登的工业软件,提供可编程的脚本能力是必不可少的能力。脚本既可以方便用户进行二次开发,也对方便对程序进行自动化测试。本文将结合

    2024年02月05日
    浏览(46)
  • 使用.net 6.0框架的WPF应用如何引用System.Windows.Forms这个dll

    在.net 6.0的WPF应用中,想使用OpenFileDialog 这个类或者FolderBrowserDialog这个类,是无法找到System.Windows.Forms这个dll引用的,即使从系统C盘里搜索到System.Windows.Forms.dll放到项目里,也会出现异常,而无法识别WPF自带的一些初始化方法。 OpenFileDialog还好,因为他有两个命名空间都有它

    2024年02月12日
    浏览(36)
  • .net wpf程序 移花接木

    最近在研究C# .net桌面程序。非常有趣 软件是国外作者写的 公司要求修改翻译 从最初的开源变成闭源再到加壳. 一路让我这个小小的职员好蛋疼. 软件是VS2015 C# WPF 程序 在不脱壳的情况下 实现挂钩类托管函数 遍历WPF控件汉化 首先通过各种手段脱壳目标程序,哪怕是不能运行

    2024年02月07日
    浏览(37)
  • WPF .NET 7.0学习整理(一)

    参照文档进行不系统的整理,看到那写到那O.o DependencyProperty:使用专有字段支持属性的标准模式的替代方法。 DependencyObject:定义了可以注册和拥有依赖属性的基类。 属性及其支持性 DependencyProperty 字段的命名约定非常重要。 字段总是与属性同名,但其后面追加了 Property 后

    2024年02月09日
    浏览(34)
  • WPF C# .NET7 基础学习

    学习视频地址:https://www.bilibili.com/video/BV1hx4y1G7C6?p=3vd_source=986db470823ebc16fe0b3d235addf050 开发工具:Visual Studio 2022 Community 基础框架:.Net 6.0 下载创建过程略 .Net和.Framework 区别是Net是依赖项,Framework是引用 1.定义一个命名空间, 最常用 不只包括using namespace,还用开头使用的usin

    2024年02月10日
    浏览(47)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包