WPF中关于RoutedEvent

RoutedEvent

CLR Event(直接事件)

  • 事件拥有者:消息的发送者,事件被触发则消息被发送
  • 事件响应者:消息的接收,处理者,通过事件处理器对事件做出响应
  • 事件订阅关系:使事件和事件处理器关联起来
  • 事件:使用 event 关键字修饰的委托类成员变量
  • 事件处理器:函数

在这种模型中,事件的响应者通过订阅关系直接关联在事件拥有者的事件上,这种模型称为CLR事件模型。

但这种模型存在弊端:每对消息都是发送–>响应关系,必须建立点对点的订阅关系,而且事件拥有者必须能够访问事件响应者,否则无法建立订阅关系,这就引出了接下来的重点,Routed Event。

RoutedEvent(路由事件)

什么是RoutedEvent

  • 功能定义:路由事件是一种可以针对元素树中的多个侦听器(而不是仅针对引发该事件的对象)调用处理程序的事件。
  • 实现定义:路由事件是一个 CLR 事件,由 RoutedEvent 类的实例提供支持并由 Windows Presentation Foundation (WPF) 事件系统处理。
  1. 类似.Net的事件,如按键、点击、鼠标移动等,但又不相同。
  2. 如果接收路由事件的元素没有处理事件,则路由事件将传播到该元素的子元素或祖先元素(取决于路由事件的路由策略)。
  3. 只要被路由的事件没有被标记为已处理,它就会被“路由”,如果一个元素将一个被路由的事件标记为已处理,则该被路由的事件将不会传播到下一个元素。

Routed Strategy

  1. Bubble(浮升):调用事件源上的事件处理程序。 路由事件随后会路由到后续的父级元素,直到到达元素树的根。字面意思,上浮,就是不断调用父级的事件处理器,直到被处理。
  2. Direct(直接):只有源元素本身才有机会调用处理程序以进行响应。但是,与标准 CLR 事件不同的是,直接路由事件支持类处理,并且可供 EventSetter 和 EventTrigger 使用。
  3. Tunnel(隧道):最初将调用元素树的根处的事件处理程序。 随后,路由事件将朝着路由事件的源节点元素(即引发路由事件的元素)方向,沿路由线路传播到后续的子元素。字面意思,就是不断调用子级的事件处理器,直到被处理。

EventSetter 和 EventTrigger

  • EventSetter:在样式中,可以通过使用 EventSetter 在标记中添加一些预先声明的 XAML 事件处理语法。 在应用样式时,所引用的处理程序会添加到带样式的实例中。
<StackPanel
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  x:Class="SDKSample.EventOvw2"
  Name="dpanel2"
  Initialized="PrimeHandledToo"
>
  <StackPanel.Resources>
    <Style TargetType="{x:Type Button}">
      <EventSetter Event="Click" Handler="b1SetColor"/>
    </Style>
  </StackPanel.Resources>
  <Button>Click me</Button>
  <Button Name="ThisButton" Click="HandleThis">
    Raise event, handle it, use handled=true handler to get it anyway.
  </Button>
</StackPanel>
CS

private void HandleThis(object sender, RoutedEventArgs e)
{
    // TODO: Add event handler implementation here.
}

private void b1SetColor(object sender, RoutedEventArgs e)
{
    // TODO: Add event handler implementation here.
}

这样做的好处在于,样式有可能包含大量可应用于应用程序中任何按钮的其他信息,将 EventSetter 添加到样式可以提高代码的重用率

  • EventTrigger:将 WPF 的路由事件和动画功能结合在一起的专用语法。可以指定当路由事件到达其路由中的某个元素(这个元素针对该事件声明了 EventTrigger)时将运行的 Storyboard。

RoutedEventArgs

  • Handled:获取或设置一个 bool 值,该值指示针对路由事件(在其经过路由时)的事件处理的当前状态。
  • OriginalSource:在父类进行任何可能的 Source 调整之前,获取由纯命中测试确定的原始报告源。在可视化树中事件源元素。
  • RoutedEvent:获取或设置与此 RoutedEventArgs 实例关联的 RoutedEvent。
  • Source:获取或设置对引发事件的对象的引用。在逻辑树中的事件源元素。

Logical Tree

代表UI的基本结构,非常匹配在 xaml 文件中声明的元素。它被用来确定一些事情:比如依赖属性值继承、资源解析等。逻辑树是静态的,在代码编写完以后,不需要程序员的干预。

Visual Tree

可视化树描述由 Visual 基类表示的可视化对象的结构。即UI中呈现给输出设备的所有元素。它被用于许多事情:如呈现、事件路由、定位资源(如果元素没有逻辑父元素)等。可视化树可以在用户切换到不同的窗口主题而简单改变。

怎么声明一个路由事件

  1. RoutedEvent 使用 RegisterRoutedEvent 该方法注册命名 xxx,并在注册期间指定路由策略。
  2. 在类的静态构造函数执行期间注册路由事件。
  3. 定义 CLR 添加 和 删除 事件访问器。
  4. 创建可以激发路由事件的方法。
代码实现

1.新建用户控件 RouteEventControl ,添加一个Button按钮,添加按钮的Click事件,XAML代码如下:

<UserControl x:Class="WpfRoutedEventDemo.RouteEventControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:WpfRoutedEventDemo"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <Grid>
        <Button Height="30" Width="100" Content="调用路由事件" Click="Button_Click"></Button>
    </Grid>
</UserControl>

2.在用户控件的隐藏文件中创建自定义路由事件,C#代码如下:

public partial class RouteEventControl : UserControl
{
    public RouteEventControl()
    {
        InitializeComponent();
    }

    //1、声明并注册路由事件,使用浮升策略
    public static readonly RoutedEvent MyClientEvent = EventManager.RegisterRoutedEvent("MyClick",
        RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(RouteEventControl));

    //2、定义 CLR 添加 和 删除 事件访问器。
    public event RoutedEventHandler MyClick
    {
        add
        {
            AddHandler(MyClientEvent, value);
        }
        remove
        {
            RemoveHandler(MyClientEvent, value);
        }
    }

    // 3、创建可以激发路由事件的方法。
    private void Button_Click(object sender, RoutedEventArgs e)
    {
        RoutedEventArgs arg = new RoutedEventArgs();
        arg.RoutedEvent = MyClientEvent;
        RaiseEvent(arg);
    }
}

3.在主界面中引入新创建的用户控件,使用自定义的路由事件MyClick,并为MyClick事件编写调用的方法,XAML代码如下:

<Window x:Class="WpfRoutedEventDemo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfRoutedEventDemo"
        mc:Ignorable="d"
        Title="RoutedEventDemo" Height="350" Width="525">
    <Grid>
        <local:RouteEventControl MyClick="RouteEventControl_MyClick"></local:RouteEventControl>
    </Grid>
</Window>

4.在主界面的隐藏文件中创建RouteEventControl_MyClick方法,C#代码如下:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    private void RouteEventControl_MyClick(object sender, RoutedEventArgs e)
    {
        MessageBox.Show("Hello:" + e.Source.ToString());
    }
}

为什么要使用路由事件

路由事件在以下场景将得到充分发挥:

  • 公用根处定义公用处理程序,例如一个标签下面放多个button标签,在父级标签上使用路由事件更加方便。
  • 合成自己的控件或者定义自己的自定义控件类。
  • 某些 WPF 样式和模板功能(如 EventSetter 和 EventTrigger)要求被引用的事件是路由事件。
  • 路由事件支持类处理机制,类可以通过该机制来指定静态方法,这些静态方法能够在任何已注册的实例处理程序访问路由事件之前,处理这些路由事件。 这在控件设计中非常有用,因为类可以强制执行事件驱动的类行为,以防它们在处理实例上的事件时被意外禁止。

AttachedEvent

  • 在 WPF 中,附加事件由 RoutedEvent 字段支持,并在引发后通过树进行路由
  • 通常,附加事件的源(引发该事件的对象)是系统或服务源,所以运行引发该事件的代码的对象并不是元素树的直接组成部分

附加事件的简单理解:

  • 路由事件的宿主全是拥有可视化实体的界面元素,附加事件需借助界面元素去与其他对象进行沟通
  • 附加事件声明的类并不是 UIElment 类的派生类,因此不具备 AddHandler 和 RemoveHandler 方法,需要手动实现
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 在WPFRoutedEventArgs是一个事件参数类,用于传递事件相关的信息。它包含了触发事件的元素、事件的路由、事件的处理状态等信息。 举个例子,我们可以通过Button控件的Click事件来理解RoutedEventArgs。当用户点击按钮时,该事件会被触发,同时会传递一个RoutedEventArgs对象作为参数。我们可以通过该对象获取到触发事件的Button控件,以及事件的路由和处理状态等信息。 以下是一个简单的示例代码: ```xml <Button Content="Click Me" Click="Button_Click"/> ``` ```csharp private void Button_Click(object sender, RoutedEventArgs e) { // 获取触发事件的Button控件 Button button = (Button)sender; // 获取事件的路由和处理状态等信息 RoutedEventArgs args = e; // ... } ``` 在上面的代码,我们通过sender参数获取到了触发事件的Button控件,通过e参数获取到了RoutedEventArgs对象,从而可以获取事件的路由和处理状态等信息。 ### 回答2: 在WPFRoutedEventArgs是一种事件参数类型,用于传递事件信息和数据。它是基于RoutedEvent的,包含了事件的原因、源对象和其他相关信息。 RoutedEventArgs包含了以下重要属性: 1. RoutedEvent:指示触发事件的路由事件的标识符。 2. Source:指示触发事件的对象。 3. OriginalSource:指示触发事件的实际用户操作开始的对象。 RoutedEventArgs主要用于在WPF处理和响应事件。例如,当用户单击一个按钮时,WPF会触发Button控件的Click事件,并将RoutedEventArgs实例作为参数传递给相关的事件处理程序。 举个例子,假设我们有一个Button控件,当用户单击按钮时,我们希望在控制台上输出“Hello WPF!”。我们可以使用以下代码来实现: ```C# // XAML代码 <Button x:Name="myButton" Content="Click Me!" Click="myButton_Click"/> // C#代码 private void myButton_Click(object sender, RoutedEventArgs e) { Console.WriteLine("Hello WPF!"); e.Handled = true; // 可以设置为true来停止事件继续向上传递 } ``` 在这个例子,我们定义了一个Button控件,并将Click事件与一个名为myButton_Click的事件处理程序关联。当用户单击按钮时,WPF将创建一个RoutedEventArgs实例,并将其作为参数传递给myButton_Click方法。 在myButton_Click方法,我们使用Console.WriteLine输出了一条消息“Hello WPF!”。我们还可以通过设置e.Handled为true来停止事件继续向上传递,以阻止其他事件处理程序对该事件做出响应。 通过理解和使用RoutedEventArgs,我们可以更好地处理和响应WPF的各种事件,从而更好地控制和交互用户界面。 ### 回答3: RoutedEventArgs是WPF(Windows Presentation Foundation)一个重要的事件参数类,用于传递事件触发所需的相关信息。它包含了事件相关的属性和方法,以及事件的源对象和目标对象等。 在WPF,事件驱动模型是非常重要的,RoutedEventArgs是一个传递事件信息的关键类。它的主要作用是在事件处理程序提供事件的详细信息,包括事件源对象以及与事件相关的其他属性。通过RoutedEventArgs实例,我们可以获取到事件触发的详细情况,如鼠标位置、键盘按键、触摸点坐标等。 举个例子,假设我们有一个Button控件,它有一个Click事件。当用户点击按钮时,该事件将触发,同时会传递一个RoutedEventArgs实例。我们可以通过该实例获取按钮的相关信息,如按钮的名称、位置、鼠标点击的坐标等。 在事件处理程序,我们通常可以通过RoutedEventArgs的各种属性来完成一些特定的操作。例如,我们可以使用RoutedEventArgs的Handled属性来决定是否终止事件的进一步传递。另外,RoutedEventArgs还提供了其他一些用于处理事件的方法和属性,如原始输入设备、事件标志位等。 总之,RoutedEventArgs是WPF重要的事件参数类,它用于传递事件触发时所需的相关信息。通过该类,我们可以获取事件源的详细信息,并进行相应的操作和处理。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值