web-dev-qa-db-ja.com

トリガーを使用してイベント引数をコマンドに渡すにはどうすればよいですか?

簡単なセットアップがあります。コマンドにバインドするPopulatingイベントを含むautocompleteboxです。私が使う

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

(これを行うためのより良い名前空間はありますか?)

それをバインドすることは大したことではありません。大したことは、そのPopulatingEventArgs引数をバインドされたコマンドに渡すことです。

では、特にPRISMおよび一般的なMVVMのベストプラクティスに従ってそれを行うにはどうすればよいですか?

17
Trident D'Gao

組み込みの方法はないので、次のようにします。

従来のインタラクショントリガーは次のように使用されます。

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

バインディングを介してEventArgsイベントのMouseEnterにアクセスすることはできないため、それを破棄する部分を変更する必要があります。
たまたま、その部分はInvokeCommandActionです。

「それで、私たちはそれをサブクラス化し、ずっと私たちを待っていた便利なメソッドをオーバーライドするつもりです」は私が書きたかったものです。しかし、クラスは封印されています。

したがって、その親(抽象)クラスをサブクラス化する必要があります:TriggerAction<DependencyObject>

最も基本的な実装は次のとおりです。

public class InteractiveCommand : TriggerAction<DependencyObject>
{
    protected override void Invoke(object parameter)
    {
    }
}

そして、そのparameterはあなたのEventArgsです!
しかし、それはそれほど単純ではありません。通常のInvokeCommandActionの動作を再現する必要があります。
Reflectorを介して逆コンパイルしました(ただし、公式ソースを参照してください。私は怠け者です)。

CommandParameter依存関係プロパティについては気にしません。InvokeCommandActionの代わりにこれを使用する場合、実際には毎回EventArgsが必要であると想定します。 。

これがフルクラスです(WPFのみ。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)
{
}

そして、それはラップです;)

EDIT: 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バージョンを使用するよりも安全性が低いことに注意してください(タイプをチェックせず、凍結された要素でクラッシュする可能性があります)。

25
Louis Kottmann

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>

...私のUserControlで。

次に、イベントハンドラーをViewModelに配置し、スコープをpublicに設定します。

public void ShowDialog(object sender, ShowDialogEventArgs e) {

}

public void AppointmentEditing(object sender, AppointmentEditingEventArgs e) {

} 

これまでのところうまく機能しています。

39
Sean Chase