Как я могу передать аргумент события команде с помощью триггеров?

Итак, у меня есть простая установка, поле автозаполнения с событием заполнения, которое я хочу привязать к команде. я использую

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

(есть ли лучшее пространство имен для этого?)

Привязать его несложно, главное передать этот аргумент PopulatingEventArgs связанной команде.

Итак, как мне это сделать в соответствии с лучшими практиками PRISM в частности и MVVM в целом?


person Trident D'Gao    schedule 26.11.2012    source источник
comment
Не ставьте теги в заголовке вопроса. См. эту ссылку о написании хороших заголовков;)   -  person Louis Kottmann    schedule 26.11.2012


Ответы (2)


Встроенного способа нет, поэтому вот как я это делаю:

Классический триггер взаимодействия используется следующим образом:

<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, см. 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)
{
}

И это обвес ;)

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 (не проверяет типы, может привести к сбою с замороженными элементами).

person Louis Kottmann    schedule 26.11.2012
comment
Я думаю, что он просто очень гибкий из коробки, что приводит к написанию различных расширений для поддержки вещей, которые мы считаем базовыми. - person Louis Kottmann; 26.11.2012
comment
Только что попробовал ваш код, хотя я понял, что сам код не работает. (это копирование/вставка из лучшего места?) В основном CommandName никогда не назначается, и поэтому ResolveCommand возвращает null, и из-за этого все это не работает. Есть также некоторые незначительные проблемы, связанные с такими вещами, как base.WritePostscript(); не определены в базовом классе, но это не важно - person Trident D'Gao; 26.11.2012
comment
Хорошо, измените свой код с помощью следующего фрагмента кода, который доказал свою эффективность: pastie.org/5437478, pastie.org/5437483 Еще раз спасибо и хорошего дня! - person Trident D'Gao; 26.11.2012
comment
@bonomo Я пробовал это только в WPF, рад видеть, что вы можете портировать его на Silverlight. - person Louis Kottmann; 26.11.2012
comment
@bonomo Я испортил код программной части, сейчас исправил. Кроме того, я попробовал ваши пирожные, и они тоже отлично работают. Обратите внимание, что мой оригинальный метод ResolveCommand() был взят из официальной библиотеки интерактивности. - person Louis Kottmann; 26.11.2012
comment
@bonomo Я обновил свой ответ, чтобы отразить ваши выводы, и добавил некоторые опасения. - person Louis Kottmann; 26.11.2012

Я попробовал 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>

... в моем пользовательском контроле.

Затем поместите обработчики событий в мою ViewModel и установите общедоступную область:

public void ShowDialog(object sender, ShowDialogEventArgs e) {

}

public void AppointmentEditing(object sender, AppointmentEditingEventArgs e) {

} 

Пока работает хорошо.

person Sean Chase    schedule 16.08.2013
comment
Это очень полезно. - person rajibdotnet; 19.06.2014
comment
Здорово!! Это позволяет избежать подклассов и позволяет использовать команды в качестве обработчиков событий из кода программной части. - person isra60; 21.04.2015
comment
Хорошее и простое решение, если вы не хотите использовать внешние (не MS) библиотеки в своем проекте. - person Shakra; 19.12.2016
comment
public void ShowDialog(object sender, ShowDialogEventArgs e) { } Мое решение, похоже, не понимает ShowDialogEventArgs. Как мне это реализовать? - person KMarto; 07.08.2017