You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

从后台代码向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,但实际事件需来自外部源。现提出以下问题:

  1. 此类实现是否可行?
  2. 若可行,如何将RoutedEvent绑定到EventTrigger?
  3. 若不可行,正确的实现方式是什么?

解答

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 16:32:06