从后台代码向WPF窗口发送自定义事件的实现方案
问题描述
我希望在EventTrigger元素中订阅由后台代码触发的自定义事件,但找到的示例均为前端触发事件的场景。以下是我编写的示例代码,预期实现每一秒触发一次动画:
后台代码
public partial class MainWindow : Window { public static readonly RoutedEvent RoutedEvent = EventManager.RegisterRoutedEvent( name: "TestRoutedEventName", routingStrategy: RoutingStrategy.Bubble, handlerType: typeof(RoutedEventHandler), ownerType: typeof(MainWindow)); public event RoutedEventHandler Event { add { AddHandler(RoutedEvent, value); } remove { RemoveHandler(RoutedEvent, value); } } public MainWindow() { InitializeComponent(); _ = new DispatcherTimer( TimeSpan.FromSeconds(1000), DispatcherPriority.Normal, (sender, args) => RaiseEvent(new RoutedEventArgs(RoutedEvent)), App.Current.Dispatcher); } }
XAML代码
<Window x:Class="WpfEventTest.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:WpfEventTest" mc:Ignorable="d" Title="MainWindow" Height="200" Width="200"> <StackPanel> <Viewbox Stretch="Fill" Width="100" Height="100"> <Canvas Width="100" Height="100"> <Path Fill="#FF0000" Name="Path"> <Path.Data> <PathGeometry Figures="M 10 10 H 90 V 90 H 10 L 10 10" FillRule="NonZero"/> </Path.Data> <Path.Triggers> <EventTrigger RoutedEvent="**WHAT DO I PUT IN HERE**"> <BeginStoryboard> <Storyboard> <ColorAnimation Storyboard.TargetName="Path" Storyboard.TargetProperty="Fill.Color" From="Red" To="White" Duration="0:0:0.8" /> </Storyboard> </BeginStoryboard> </EventTrigger> </Path.Triggers> </Path> </Canvas> </Viewbox> </StackPanel> </Window>
目前我仅为简化使用DispatcherTimer,但实际事件需来自外部源。现提出以下问题:
- 此类实现是否可行?
- 若可行,如何将RoutedEvent绑定到EventTrigger?
- 若不可行,正确的实现方式是什么?
解答
1. 此类实现是否可行?
可行,但现有代码存在两处关键问题需要修正:
DispatcherTimer的时间参数错误:TimeSpan.FromSeconds(1000)会设置为1000秒触发一次,而非预期的1秒,需改为TimeSpan.FromSeconds(1)。- 自定义路由事件的定义与触发逻辑不匹配:当前事件注册名称为
TestRoutedEventName,但公开的事件属性名为Event,容易混淆;且触发事件时未指定源元素,导致Path无法接收到冒泡的事件。
2. 如何将RoutedEvent绑定到EventTrigger?
按以下步骤修改代码即可实现绑定:
步骤1:修正后台代码
统一事件命名,并在触发时指定事件源为Path,确保事件能路由到目标元素:
public partial class MainWindow : Window { // 统一事件名称,避免混淆 public static readonly RoutedEvent TestRoutedEvent = EventManager.RegisterRoutedEvent( name: nameof(TestRoutedEvent), routingStrategy: RoutingStrategy.Bubble, handlerType: typeof(RoutedEventHandler), ownerType: typeof(MainWindow)); // 公开的事件属性与注册的路由事件名称对应 public event RoutedEventHandler TestRoutedEvent { add { AddHandler(TestRoutedEvent, value); } remove { RemoveHandler(TestRoutedEvent, value); } } public MainWindow() { InitializeComponent(); _ = new DispatcherTimer( TimeSpan.FromSeconds(1), // 修正为1秒触发一次 DispatcherPriority.Normal, (sender, args) => { // 指定事件源为Path,让事件从Path开始冒泡,确保Path的EventTrigger能捕获到 RaiseEvent(new RoutedEventArgs(TestRoutedEvent, Path)); }, Dispatcher); } }
步骤2:修改XAML中的EventTrigger
使用命名空间前缀引用自定义路由事件,替换**WHAT DO I PUT IN HERE**为local:MainWindow.TestRoutedEvent:
<Path.Triggers> <EventTrigger RoutedEvent="local:MainWindow.TestRoutedEvent"> <BeginStoryboard> <Storyboard> <ColorAnimation Storyboard.TargetProperty="Fill.Color" From="Red" To="White" Duration="0:0:0.8" /> </Storyboard> </BeginStoryboard> </EventTrigger> </Path.Triggers>
注意:
Path.Triggers中的EventTrigger默认只能响应该元素本身触发或路由到该元素的事件,因此触发时指定源为Path是关键。
3. 替代实现方式(更简洁)
如果觉得自定义路由事件过于繁琐,可使用DataTrigger绑定依赖属性的方式,更直观易维护:
后台代码
添加一个用于触发动画的依赖属性,并在定时器中切换其值:
public partial class MainWindow : Window { public bool TriggerAnimation { get => (bool)GetValue(TriggerAnimationProperty); set => SetValue(TriggerAnimationProperty, value); } public static readonly DependencyProperty TriggerAnimationProperty = DependencyProperty.Register(nameof(TriggerAnimation), typeof(bool), typeof(MainWindow), new PropertyMetadata(false)); public MainWindow() { InitializeComponent(); _ = new DispatcherTimer( TimeSpan.FromSeconds(1), DispatcherPriority.Normal, (sender, args) => { // 切换属性值触发DataTrigger TriggerAnimation = !TriggerAnimation; }, Dispatcher); } }
XAML代码
使用Style中的DataTrigger绑定依赖属性,触发动画:
<Path Fill="#FF0000" Name="Path"> <Path.Data> <PathGeometry Figures="M 10 10 H 90 V 90 H 10 L 10 10" FillRule="NonZero"/> </Path.Data> <Path.Style> <Style TargetType="Path"> <Style.Triggers> <DataTrigger Binding="{Binding TriggerAnimation, RelativeSource={RelativeSource AncestorType=Window}}" Value="True"> <DataTrigger.EnterActions> <BeginStoryboard> <Storyboard> <ColorAnimation Storyboard.TargetProperty="Fill.Color" From="Red" To="White" Duration="0:0:0.8" /> </Storyboard> </BeginStoryboard> </DataTrigger.EnterActions> </DataTrigger> </Style.Triggers> </Style> </Path.Style> </Path>
内容的提问来源于stack exchange,提问作者Sprintstar
相关产品推荐
相关产品推荐

