WPF中文网

附加事件实战

本节课我们将演示附加事件的用法。假定我们有一个自定义控件,实时显示公司的销售额,在达到公司的既定销售目标后,每增加50万的业绩并触发一个事件,表示可以发绩效奖金啦。

我们修改一下《路由事件实战》中的Demo,在其中添加一个SalesManager类,并在类中定义一个附加事件。如下所示:

namespace HelloWorld.MVVM
{
    public class SalesManager
    {
        public static readonly RoutedEvent CheckEvent = EventManager.RegisterRoutedEvent(
            "CheckEvent", 
            RoutingStrategy.Bubble, 
            typeof(RoutedEventHandler), 
            typeof(SalesManager));

        public static void AddCheckHandler(DependencyObject dependencyObject, RoutedEventHandler handler)
        {
            if (dependencyObject is UIElement uiElement)
            {
                uiElement.AddHandler(CheckEvent, handler);
            }            
        }

        public static void RemoveCheckHandler(DependencyObject dependencyObject, RoutedEventHandler handler)
        {
            if (dependencyObject is UIElement uiElement)
            {
                uiElement.RemoveHandler(CheckEvent, handler);
            }
        }
    }
}

在上面的代码中,我们定义了一个CheckEvent路由事件,并通过AddCheckHandler添加到事件系统中。然后在主窗体的XMAL代码中去使用这个Check附加事件。

<controls:Widget Value="{Binding ElementName=slider,Path=Value}" 
                 Target="1000000"
                 Completed="Widget_Completed"
                 helper:SalesManager.Check="Widget_Check"
                 Title="第四季度北美市场总销售额统计" 
                 Canvas.Left="116" Canvas.Top="17" 
                 Height="103" Width="269"/>

请注意,在使用前引入SalesManager的命名空间

xmlns:helper="clr-namespace:HelloWorld.MVVM"

在前面的课程中,Widget控件的Completed事件表示完成销售目标是引发的事件,所以,我们要在这个事件里去引发SalesManager.Check附加事件,因为题目已设定必须是完成了销售目标后,每增加50万业绩才发奖金。

private void Widget_Completed(object sender, RoutedEventArgs e)
{
    Widget widget = sender as Widget;
    listBox.Items.Insert(0, $"完成目标销售额:{widget.Value}");
    widget.RaiseEvent(new RoutedEventArgs(SalesManager.CheckEvent));
}

private void Widget_Check(object sender, RoutedEventArgs e)
{
    Widget widget = sender as Widget;
    if (((int)widget.Value) % 500000 < 5000)
    {
        listBox.Items.Insert(0, $"当前业绩:{widget.Value},每累计50万发奖金啦:{widget.Value*0.5}");
    }
}

所以,我们在Widget_Completed回调函数中,让widget自己去引发它的附加事件。并在Widget_Check回调函数中去判断当前的业绩有没有增加到50万。当然这里只是写了一个判断的伪代码。最后我们运行一下本程序,拖动Slider控件,模拟销售业绩的增长,以观察结果。

当前课程源码下载:(注明:本站所有源代码请按标题搜索)

文件名:088-《附加事件实战》-源代码.rar
链接:https://pan.baidu.com/s/1yu-q4tUtl0poLVgmcMfgBA
提取码:wpff

——重庆教主 2023年10月27日

在上一节我们已经学了在控件(元素)中定义路由事件,而在非元素的类型中定义的路由事件,我们称为附加事件。所以,本质上讲,附加事件也是路由事件。

附加事件虽然定义在非元素的类型中,但却是在元素类型中引发,并且通过元素树进行传播。

附加事件其实与附加属性相似,但是不完全相同。定义附加事件时,分两个步骤,第一步是利用EventManager的RegisterRoutedEvent方法成员定义一个路由事件。

public static readonly RoutedEvent 路由事件名称 = EventManager.RegisterRoutedEvent(
            name: "路由事件名称",
            routingStrategy: 冒泡事件/隧道事件/直接事件,
            handlerType: 路由事件委托的反射实例,
            ownerType: 路由事件拥有者的反射实例;

第二步是将路由事件名称包装一下,它的包装有点特别。

public static void Add附加事件名称Handler(DependencyObject dependencyObject, RoutedEventHandler handler)
{
    if (dependencyObject is UIElement uiElement)
    {
        uiElement.AddHandler(路由事件名称, handler);
    }            
}

public static void Remove附加事件名称Handler(DependencyObject dependencyObject, RoutedEventHandler handler)
{
    if (dependencyObject is UIElement uiElement)
    {
        uiElement.RemoveHandler(路由事件名称, handler);
    }
}

将路由事件名称包装成附回事件名称,需要定义两个静态方法成员,且成员命名规则必须如下定义:

Add<附加事件名称>Handler

Remove<附加事件名称>Handler

如此,WPF的事件系统便会通过内部反射机制知道开发者的意图。在添加和删除附加事件的定义中,第一个参数DependencyObject表示附加事件的宿主对象,第二个参数RoutedEventHandler表示一个委托,由外界传入,即当附加事件被触发时所执行的回调函数。

最后一点,谁附加了一个附加事件,谁就负责触发和编写触发后的回调函数代码逻辑。通常是某个控件去附加一个事件,因为控件的基类都是UIElement类,而只有UIElement才具有添加路由事件、删除路由事件、触发路由事件的功能。

  • UIElement.AddHandler();添加一个路由事件;
  • UIElement.RemoveHandler();删除一个路由事件;
  • UIElement.RaiseEvent();触发一个路由事件;

在了解了这些关于附加事件的基础常识后,我们就可以用一个实际的例子来演示它的用法。下一节,附加事件的实战。

——重庆教主 2023年10月27日

copyright @重庆教主 WPF中文网 联系站长:(QQ)23611316 (微信)movieclip (QQ群).NET小白课堂:864486030 | 本文由WPF中文网原创发布,谢绝转载 渝ICP备2023009518号-1