我正在尝试使用附加事件来调用
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>
所以重申我的问题,我在这里想要实现的目标可能吗?难道就不能以这种方式使用附加事件吗?
谢谢。
据我了解,
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
来添加事件。
这是一个
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>