是否可以使用带有附加事件的 Microsoft.Xaml.Behaviors.EventTrigger 来调用命令?

问题描述 投票:0回答:2

我正在尝试使用附加事件来调用

ICommand

我正在使用

Microsoft.Toolkit.Mvvm
NuGet 包以及
Microsoft.Xaml.Behaviors.Wpf
Nuget 包。

通过定义

<BeginStoryBoardAction />
并将
<FOO.Triggers />
设置为等于相关附加事件的名称,我已成功使用
<EventTrigger />
中的
RoutedEvent
启动故事板。

但是,据我所知,无法使用

ICommand
中提供的任何内容来调用
<EventTrigger />
。我的意思是,我无法在
<EventTriggers.Actions />
块的主体内使用任何东西(类似于
<Behaviors.InvokeCommandAction />
),这将导致
ICommand
被调用。

按照最小、完整和可验证的示例,为了演示我想要实现的目标,您可以参考 GitHub 上的项目 - https://github.com/AbbottWC/MCVE_AttachedEventFailure

或者

  • 打开 Visual Studio。创建 WPF 应用程序(针对 .Net Core 3.1)

  • 工具 -> NuGet 包管理器 -> 管理此解决方案的包

  • 添加 Microsoft.Toolkit.Mvvm(针对

    RelayCommand
    类)和
    Microsoft.Xaml.Behaviors.Wpf
    包。

  • App.Xaml.cs

     private RelayCommand testCommand = new RelayCommand(( ) => MessageBox.Show("Event Captured!", "Success!"));
     public RelayCommand TestCommand => testCommand;
    
  • 在MainWindow.xaml中

  • 定义命名空间

    xmlns:i="http://schemas.microsoft.com/xaml/behaviors"

  • local
    重命名为
    l

  • 将以下内容添加到 XAML 正文中的

    </Window>
    结束标记之前:

<i:Interaction.Triggers>
    <!--This will work, and is present only to prove the problem lies not with the Command or how it is being accessed.-->
    <i:EventTrigger EventName="MouseEnter">
        <i:EventTrigger.Actions>
            <i:InvokeCommandAction Command="{Binding TestCommand, Source={x:Static l:App.Current}}" />
        </i:EventTrigger.Actions>
    </i:EventTrigger>
    <!--This will not work, and is meant to provide an example of the problem.-->
    <i:EventTrigger EventName="Mouse.MouseEnter">
        <i:EventTrigger.Actions>
            <i:InvokeCommandAction Command="{Binding TestCommand, Source={x:Static l:App.Current}}" />
        </i:EventTrigger.Actions>
    </i:EventTrigger>
</i:Interaction.Triggers>

所以重申我的问题,我在这里想要实现的目标可能吗?难道就不能以这种方式使用附加事件吗?

谢谢。

c# wpf xaml mvvm
2个回答
2
投票

据我了解,

EventTrigger
只能监听源对象“拥有”的事件。因此,要回答您的问题,使用
EventTrigger
类是不可能的。

如果您查看,当您传递

Mouse.MouseEnter
时,它会尝试从目标类型获取该事件。另外,由于您没有指定目标,因此它默认为
AssociatedObject
,在您的情况下为
Window

Type targetType = obj.GetType();
EventInfo eventInfo = targetType.GetEvent(eventName);

现在我发现的问题是,如果它找不到事件,它只会在源对象不为空时抛出异常,并且在您的情况下您没有指定一个异常,因此它会默默地失败。不知道设计师为什么这样做。

现在,我确实找到了这篇博文,其中准确描述了如何解决此问题: https://sergecalderara.wordpress.com/2012/08/23/how-to-attached-an-mvvm-eventtocommand-to-an-attached-event/

基本上,您继承自

EventTriggerBase<T>
,并且在
OnAttached
方法中,您需要在
AddHandler
上调用
AssociatedObject
来添加事件。


0
投票

这是一个

RoutedEventTrigger
类,应该可以完成工作。

using Microsoft.Xaml.Behaviors;
using System.Windows;
//..

public class RoutedEventTrigger : TriggerBase<DependencyObject>
{
    public RoutedEvent TargetEvent
    {
        get { return (RoutedEvent)GetValue(TargetEventProperty); }
        set { SetValue(TargetEventProperty, value); }
    }

    public static readonly DependencyProperty TargetEventProperty =
        DependencyProperty.Register("TargetEvent", typeof(RoutedEvent), 
            typeof(RoutedEventTrigger), new PropertyMetadata(null, HandleEventChanged));

    private RoutedEventHandler _handler;
    protected override void OnAttached()
    {
        base.OnAttached();
        RegisterEvent();
    }

    protected override void OnDetaching()
    {
        base.OnDetaching();
        UnregisterEvent();
    }

    private void OnEventImpl(object sender, RoutedEventArgs eventArgs)
    {
        InvokeActions(eventArgs);
    }


    private static void HandleEventChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if (d is not RoutedEventTrigger trigger) return;
        trigger.HandleEventChanged(e);
    }

    private void HandleEventChanged(DependencyPropertyChangedEventArgs e)
    {
        if (e.OldValue is RoutedEvent oev)
            UnregisterEvent(oev);

        if (e.NewValue is RoutedEvent nev)
            RegisterEvent(nev);
    }

    private void RegisterEvent(RoutedEvent ev = null)
    {
        if (AssociatedObject is not UIElement el) return;
        if (ev == null) ev = TargetEvent;

        if (ev != null)
        {
            _handler = new RoutedEventHandler(OnEventImpl);
            el.AddHandler(ev, _handler);
        }
    }

    private void UnregisterEvent(RoutedEvent ev = null)
    {
        if (ev == null) ev = TargetEvent;

        if (AssociatedObject is UIElement el && ev != null && _handler != null)
        {
            el.RemoveHandler(ev, _handler);
        }

        _handler = null;
    }
}

以下是如何在您的

XAML
中使用它。

<i:Interaction.Triggers>
    <ui:RoutedEventTrigger TargetEvent="{x:Static Mouse.MouseEnterEvent}">
        <prism:InvokeCommandAction Command="{Binding TestCommand}" />
    </ui:RoutedEventTrigger>
</i:Interaction.Triggers>
© www.soinside.com 2019 - 2024. All rights reserved.