Хобрук: Ваш путь к мастерству в программировании

Как отправить второй параметр обработчика событий при обработке события в MVVM?

У меня есть TreeView. Есть два параметра treeView_Expanded(object sender, RoutedEventArgs e), когда событие TreeViewItem.Expanded обрабатывается с использованием кода программной части:

XAML:

<TreeView Name="treeView" TreeViewItem.Expanded="treeView_Expanded"/> 

Код программной части:

private void treeView_Expanded(object sender, RoutedEventArgs e)
{ 
     //***I take THE SECOND PARAMETER to work ***
     TreeViewItem item = e.Source as TreeViewItem;

}

Однако, когда я обрабатываю событие TreeViewItem.Expanded с помощью правил MVVM, я всегда беру первый параметр object sender. Но я хотел бы взять второй параметр RoutedEventArgs e:

XAML:

<TreeView ItemsSource="{Binding Person}">
   <i:Interaction.Triggers>
      <helper:RoutedEventTrigger RoutedEvent="TreeViewItem.Expanded">
         <prism:InvokeCommandAction Command="{Binding GetNewTreeViewItemCommand}"/>
      </helper:RoutedEventTrigger>
   </i:Interaction.Triggers>
</TreeView>

Модель представления:

public DelegateCommand<RoutedEventArgs> GetNewTreeViewItemCommand { get; set; }
public MainWindowViewModel()
{
   GetNewTreeViewItemCommand = new DelegateCommand<RoutedEventArgs>(LoadNewTreeViewITem);
}

private void LoadNewTreeViewITem(RoutedEventArgs e)
{
   //e is "object sender"(the FIRST parameter), but I want to take  
   //RoutedEventArgs e(the SECOND parameter)             
}           

Как я могу отправить второй параметр при обработке события в MVVM?


  • В вашем InvokeCommandAction установите CommandParameter на все, что вы хотите отправить в функцию. Я не знаю, как привязаться к RoutedEvent, но с помощью CommandParameter вы бы его отправили. CommandParameter — это свойство, которое вы можете установить в xaml везде, где вы можете установить свойство Command. 14.03.2016
  • @GordonAllocman да. Я пытался использовать «CommandParameter», но как установить второй параметр? 14.03.2016
  • Затем используйте свою команду в своем коде, т.е. private void treeView_Expanded(object sender, RoutedEventArgs e) { vm.GetNewTreeViewItemCommand.Execute(new object[] {sender,e}); } 14.03.2016
  • Кстати, передача элемента пользовательского интерфейса (TreeViewItem) вашему VM — плохая практика. 14.03.2016
  • @XAMlMAX, если вы можете показать, как я могу справиться, не отправляя TreeViewItem, я могу проголосовать за ваш ответ. Не стесняйтесь отвечать. 14.03.2016
  • Просто переместите метод из ViewModel в код позади. Если вы добавляете элементы в свою коллекцию VM, создайте команду для этого, если вы получаете дополнительную информацию для этого человека, тогда создайте метод в VM для возврата этой информации, за исключением того, что больше нечего делать. 14.03.2016
  • @XAMlMAX Тем не менее, TreeViewItem будет использоваться в модели представления :) 14.03.2016

Ответы:


1

Вы можете использовать другой TriggerAction для вызова команды в вашей виртуальной машине, которая также будет передавать аргументы:

<TreeView ItemsSource="{Binding Person}">
  <i:Interaction.Triggers>
     <helper:RoutedEventTrigger RoutedEvent="TreeViewItem.Expanded">
        <local:CustomCommandAction Command="{Binding GetNewTreeViewItemCommand}"/>
     </helper:RoutedEventTrigger>
  </i:Interaction.Triggers>

CustomCommandAction

public sealed class CustomCommandAction : TriggerAction<DependencyObject>
{
    public static readonly DependencyProperty CommandParameterProperty =
        DependencyProperty.Register("CommandParameter", typeof(object), typeof(CustomCommandAction), null);

    public static readonly DependencyProperty CommandProperty = DependencyProperty.Register(
        "Command", typeof(ICommand), typeof(CustomCommandAction), null);

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

    public object CommandParameter
    {
        get
        {
            return this.GetValue(CommandParameterProperty);
        }

        set
        {
            this.SetValue(CommandParameterProperty, value);
        }
    }

    protected override void Invoke(object parameter)
    {
        if (this.AssociatedObject != null)
        {
            ICommand command = this.Command;
            if (command != null)
            {
                if (this.CommandParameter != null)
                {
                    if (command.CanExecute(this.CommandParameter))
                    {
                        command.Execute(this.CommandParameter);
                    }
                }
                else
                {
                    if (command.CanExecute(parameter))
                    {
                        command.Execute(parameter);
                    }
                }
            }
        }
    }
}

ОБНОВЛЕНИЕ

XAML

    <TreeView ItemsSource="{Binding Person}" Grid.Row="1" >
        <TreeView.ItemTemplate>
            <HierarchicalDataTemplate ItemsSource="{Binding Children}">
                <StackPanel Orientation="Horizontal">
                    <TextBlock Text="{Binding Path=Description}" />
                </StackPanel>
            </HierarchicalDataTemplate>
        </TreeView.ItemTemplate>
        <i:Interaction.Triggers>
            <local:RoutedEventTrigger RoutedEvent="TreeViewItem.Expanded">
                <local:CustomCommandAction Command="{Binding GetNewTreeViewItemCommand}"/>
            </local:RoutedEventTrigger>
        </i:Interaction.Triggers>
    </TreeView>

VM

    private ObservableCollection<Person> _people = new ObservableCollection<Person>();

    public ObservableCollection<Person> Person
    {
        get { return _people; }
    }


    private DelegateCommand _getNewTreeViewItemCommand = null;

    public ICommand GetNewTreeViewItemCommand { get { return _getNewTreeViewItemCommand; } }


    private void LoadNewTreeViewITem(object param)
    {
        var tuple = (Tuple<object, object>)param;

        object sender = tuple.Item1;
        RoutedEventArgs e = tuple.Item2 as RoutedEventArgs;

        System.Diagnostics.Debug.WriteLine(sender);
        System.Diagnostics.Debug.WriteLine(e.RoutedEvent);
    }

    public MainWindowViewModel()
    {
        _getNewTreeViewItemCommand = new DelegateCommand(LoadNewTreeViewITem, (o) => true);

        for (int i = 0; i < 10; i++)
        {
            var newPerson = new Person() { Description = i.ToString() };
            for (int j = 0; j < 10; j++)
            {
                newCode.Children.Add(new Person() { Description = i.ToString() + j.ToString() });
            }

            _people.Add(newCode);
        }
    }

Человек

public class Person
{
    public string Description { get; set; }

    public ObservableCollection<Person> Children { get; set; } = new ObservableCollection<Person>();
}

Команда

public class DelegateCommand : ICommand
{
    private readonly Action<object> _execute;
    private readonly Func<object, bool> _canExecute;

    public event EventHandler CanExecuteChanged;


    public DelegateCommand(Action<object> execute, Func<object, bool> canExecute)
    {
        _execute = execute;
        _canExecute = canExecute;
    }

    public bool CanExecute(object parameter)
    {
        return _canExecute(parameter);
    }

    public void Execute(object parameter)
    {
        _execute(parameter);
    }
}

Маршрутедевенттриггер

public class RoutedEventTrigger : EventTriggerBase<DependencyObject>
{
    RoutedEvent _routedEvent;

    public RoutedEvent RoutedEvent
    {
        get { return _routedEvent; }
        set { _routedEvent = value; }
    }

    public RoutedEventTrigger()
    {
    }
    protected override void OnAttached()
    {
        Behavior behavior = base.AssociatedObject as Behavior;
        FrameworkElement associatedElement = base.AssociatedObject as FrameworkElement;

        if (behavior != null)
        {
            associatedElement = ((IAttachedObject)behavior).AssociatedObject as FrameworkElement;
        }
        if (associatedElement == null)
        {
            throw new ArgumentException("Routed Event trigger can only be associated to framework elements");
        }
        if (RoutedEvent != null)
        {
            associatedElement.AddHandler(RoutedEvent, new RoutedEventHandler(this.OnRoutedEvent));
        }
    }
    void OnRoutedEvent(object sender, RoutedEventArgs args)
    {
        base.OnEvent(args);
    }
    protected override string GetEventName()
    {
        return RoutedEvent.Name;
    }
}

CustomCommandAction

public sealed class CustomCommandAction : TriggerAction<DependencyObject>
{
    public static readonly DependencyProperty CommandParameterProperty =
        DependencyProperty.Register("CommandParameter", typeof(object), typeof(CustomCommandAction), null);

    public static readonly DependencyProperty CommandProperty = DependencyProperty.Register(
        "Command", typeof(ICommand), typeof(CustomCommandAction), null);

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

    public object CommandParameter
    {
        get
        {
            return this.GetValue(CommandParameterProperty);
        }

        set
        {
            this.SetValue(CommandParameterProperty, value);
        }
    }

    protected override void Invoke(object parameter)
    {
        if (this.AssociatedObject != null)
        {
            ICommand command = this.Command;
            if (command != null)
            {
                if (this.CommandParameter != null)
                {
                    if (command.CanExecute(this.CommandParameter))
                    {
                        command.Execute(this.CommandParameter);
                    }
                }
                else
                {
                    if (command.CanExecute(parameter))
                    {
                        command.Execute(new Tuple<object, object>(this.AssociatedObject, parameter));
                    }
                }
            }
        }
    }
}
14.03.2016
  • Вам нужно использовать это CustomCommandAction вместо prism:InvokeCommandAction, и оно должно работать. Если вам нужно иметь 2 параметра, вы можете изменить строку выполнения команды на что-то вроде этого: command.Execute(new Tuple‹object, object›(this.AssociatedObject, параметр)); 14.03.2016
  • И в вашем методе LoadNewTreeViewITem просто извлеките нужные вам объекты из этого кортежа: LoadNewTreeViewITem(object param) { var tuple = (Tuple‹object, object›)param; RoutedEventArgs e = tuple[1] as RoutedEventArgs; ...} 14.03.2016
  • Я пробовал ваш подход, но он дает первый аргумент, а не второй. Теперь я попробую использовать Tuple<object, object> 14.03.2016
  • пожалуйста, напишите полный пример. Я не могу понять, что я делаю неправильно. Заранее спасибо. 14.03.2016
  • Ок, напишу полный пример, нп) 14.03.2016
  • Обновлен ответ, так что и отправитель, и аргументы доступны в LoadNewTreeViewITem. 14.03.2016
  • Спасибо! упс. Я скопировал весь ваш код, но у меня ошибка cannot convert from 'method group' to 'Action'' 14.03.2016
  • Возможно, это потому, что я использовал свою собственную реализацию DelegateCommand для этого примера. В какой строке вы получаете эту ошибку? 14.03.2016
  • Не могли бы вы изменить свою DelegateCommand‹RoutedEventArgs› GetNewTreeViewItemCommand { get; задавать; } to DelegateCommand‹object› GetNewTreeViewItemCommand { get; задавать; } 14.03.2016
  • чувак, ты потрясающий! :) Это работает как шарм! Спасибо большое!:) 15.03.2016
  • Новые материалы

    Аргументы прогрессивного улучшения почти всегда упускают суть
    В наши дни в кругах веб-разработчиков много болтают о Progressive Enhancement — PE, но на самом деле почти все аргументы с обеих сторон упускают самую фундаментальную причину, по которой PE..

    Введение в Джанго Фреймворк
    Схема «работать умно, а не усердно» В этой и последующих статьях я познакомлю вас с тем, что такое фреймворк Django и как создать свое первое приложение с помощью простых и понятных шагов, а..

    Настольный ПК как «одно кольцо, чтобы править всеми» домашних компьютеров
    Вид после 9 месяцев использования С настольных компьютеров все началось, но в какой-то момент они стали «серверами», и мы все перешли на ноутбуки. В прошлом году я столкнулся с идеей настольных..

    Расширенные методы безопасности для VueJS: реализация аутентификации без пароля
    Руководство, которое поможет вам создавать безопасные приложения в долгосрочной перспективе Безопасность приложений часто упускается из виду в процессе разработки, потому что основная..

    стройный-i18следующий
    Представляем стройную оболочку для i18next. Эта библиотека, основанная на i18next, заключает экземпляр i18next в хранилище svelte и отслеживает события i18next, такие как languageChanged,..

    Обзор 20 основных и современных методов работы с массивами в JavaScript
    Вы знаете их всех? В этом коротком посте я покажу сводку методов, доступных в JavaScript для работы с массивами. Я надеюсь, что вы найдете это полезным! В конце поста вы найдете ссылку на..

    Да, но я чувствую необходимость указать, что это или не единственные два.
    Да, но я чувствую необходимость указать, что это или не единственные два. Обучение с подкреплением (в качестве примера) также является важным.