如何使用触发器将事件参数传递给命令?

17

我有一个简单的设置,其中包含一个自动完成框和其Populating事件,我想将其绑定到一个命令。我使用

clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity 

(有更好的命名空间来完成这个吗?)

绑定并不是什么大不了的事情,关键是将PopulatingEventArgs参数传递给绑定的命令。

那么我该如何按照PRISM的最佳实践和MVVM的一般惯例来做呢?


请勿在问题标题中添加标签。请参阅此链接以了解如何编写好的标题 ;) - Louis Kottmann
2个回答

40

我尝试使用InteractiveCommand,但遇到了问题。相反,我设置了对Microsoft.Expression.Interactions的引用并包含了相关内容。

转换后:

我尝试了InteractiveCommand,但是它给我带来了问题。相反,我设置了对Microsoft.Expression.Interactions的引用并包含了相关内容。

xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions" 

<i:Interaction.Triggers>
    <i:EventTrigger EventName="AppointmentEditing">
        <ei:CallMethodAction MethodName="AppointmentEditing" TargetObject="{Binding}" />
    </i:EventTrigger>
    <i:EventTrigger EventName="ShowDialog">
        <ei:CallMethodAction MethodName="ShowDialog" TargetObject="{Binding}" />
    </i:EventTrigger>
</i:Interaction.Triggers>

...在我的用户控件中。

接下来将事件处理程序放在我的视图模型中,并将作用域设置为公共:

public void ShowDialog(object sender, ShowDialogEventArgs e) {

}

public void AppointmentEditing(object sender, AppointmentEditingEventArgs e) {

} 

目前工作良好。


2
这非常有帮助。 - rajibdotnet
6
太好了!这样可以避免子类化,并允许从后台代码中使用命令作为事件处理程序。 - isra60
如果您不想在项目中使用外部(非 MS)库,那么这是一个简单而好的解决方案。 - Shakra
`public void ShowDialog(object sender, ShowDialogEventArgs e) {}我的解决方案似乎不理解ShowDialogEventArgs`。我应该如何实现它? - KMarto

25

没有内置的方法,这是我如何做的:

经典的交互触发器用法如下:

<Button Content="I am a button">
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="MouseEnter">
            <i:InvokeCommandAction Command="{Binding CommandWithNoArgs}" />
        </i:EventTrigger>
    </i:Interaction.Triggers>
</Button>
我们无法通过绑定访问MouseEnter事件的EventArgs,因此我们需要修改丢弃它的代码段。恰好,这个代码段是InvokeCommandAction
我本来想写“所以我们只需对其进行子类化并重写一个便捷的方法”,但该类是密封的。
因此,我们需要对其父(抽象)类进行子类化:TriggerAction<DependencyObject> 最基本的实现如下:
public class InteractiveCommand : TriggerAction<DependencyObject>
{
    protected override void Invoke(object parameter)
    {
    }
}

那个参数就是你的EventArgs!但是要注意,这并不简单,我们需要复制正常的InvokeCommandAction的行为。通过反编译器Reflector,我对其进行了反编译(但你也可以查看官方源代码,我只是懒得去)。

我们将不关心CommandParameter依赖属性,我们假设如果你使用这个而不是InvokeCommandAction,则每次都想要EventArgs

以下是完整的类(仅适用于WPF,请参见EDIT以了解SilverLight):

public class InteractiveCommand : TriggerAction<DependencyObject>
{
    protected override void Invoke(object parameter)
    {
        if (base.AssociatedObject != null)
        {
            ICommand command = this.ResolveCommand();
            if ((command != null) && command.CanExecute(parameter))
            {
                command.Execute(parameter);
            }
        }
    }

    private ICommand ResolveCommand()
    {
        ICommand command = null;
        if (this.Command != null)
        {
            return this.Command;
        }
        if (base.AssociatedObject != null)
        {
            foreach (PropertyInfo info in base.AssociatedObject.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance))
            {
                if (typeof(ICommand).IsAssignableFrom(info.PropertyType) && string.Equals(info.Name, this.CommandName, StringComparison.Ordinal))
                {
                    command = (ICommand)info.GetValue(base.AssociatedObject, null);
                }
            }
        }
        return command;
    }

    private string commandName;
    public string CommandName
    {
        get
        {
            base.ReadPreamble();
            return this.commandName;
        }
        set
        {
            if (this.CommandName != value)
            {
                base.WritePreamble();
                this.commandName = value;
                base.WritePostscript();
            }
        }
    }

    #region Command
    public ICommand Command
    {
        get { return (ICommand)GetValue(CommandProperty); }
        set { SetValue(CommandProperty, value); }
    }

    // Using a DependencyProperty as the backing store for Command.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty CommandProperty =
        DependencyProperty.Register("Command", typeof(ICommand), typeof(InteractiveCommand), new UIPropertyMetadata(null));
    #endregion
}

同任何从互联网获取的代码一样,我强烈建议你阅读整个类并尝试理解它的作用。不要只是将其丢到您的应用程序中。

现在我们可以执行:

<Button Content="I am a button">
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="MouseEnter">
            <local:InteractiveCommand Command="{Binding CommandWithEventArgs}" />
        </i:EventTrigger>
    </i:Interaction.Triggers>
</Button>

然后是背后的代码:

#region CommandWithEventArgs
DelegateCommand<MouseEventArgs> _CommandWithEventArgs;
/// <summary>
/// Exposes <see cref="CommandWithEventArgs(MouseEventArgs)"/>.
/// </summary>
public DelegateCommand<MouseEventArgs> CommandWithEventArgs
{
    get { return _CommandWithEventArgs ?? (_CommandWithEventArgs = new DelegateCommand<MouseEventArgs>(CommandWithEventArgs)); }
}
#endregion
public void CommandWithEventArgs(MouseEventArgs param)
{
}

完美收官;)

编辑:对于SilverLight,请改用以下代码:

    public class InteractiveCommand : TriggerAction<DependencyObject>
    {
        protected override void Invoke(object parameter)
        {
            if (base.AssociatedObject != null)
            {
                ICommand command = Command;
                if ((command != null) && command.CanExecute(parameter))
                {
                    command.Execute(parameter);
                }
            }
        }

        #region Command
        public ICommand Command
        {
            get { return (ICommand)GetValue(CommandProperty); }
            set { SetValue(CommandProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Command.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty CommandProperty =
            DependencyProperty.Register("Command", typeof(ICommand), typeof(InteractiveCommand), new UIPropertyMetadata(null));
        #endregion
    }

请注意,这种方法不如使用完整版的 WPF 安全(不检查类型,可能会在冻结元素时崩溃)。


我认为它在初始状态下非常灵活,这导致我们需要编写各种扩展来支持我们认为是基本的功能。 - Louis Kottmann
刚试了一下你的代码,即使我明白了其中的思路,但是代码本身并没有起作用。(是从其他地方复制/粘贴来的吗?)基本上CommandName从未被赋值,因此ResolveCommand返回null,并且整个程序因此无法正常工作。还有一些小问题,例如base.WritePostscript();没有在基类中定义,但这不重要。 - Trident D'Gao
好的,请使用已被证明可行的以下代码片段修改您的代码:http://pastie.org/5437478,http://pastie.org/5437483。再次感谢您,祝您有美好的一天! - Trident D'Gao
@bonomo 我只在WPF中尝试过它,很高兴看到你能将它移植到Silverlight中。 - Louis Kottmann
@bonomo 我之前搞砸了代码,现在已经修复了。另外,我尝试了你的 pasties,它们也正常工作。请注意,我的原始 ResolveCommand() 方法是从官方的 Interactivity 库中获取的。 - Louis Kottmann
@bonomo,我更新了我的答案以反映你的发现,并添加了一些关注点。 - Louis Kottmann

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接