Привязать пользовательский метод с параметрами в ViewModel

Я создаю приложение UWP MVVM. Я создал ViewModel и View, сделал DataContext, и все отлично работает с привязками.

Мне удалось вызвать метод без параметров. Это мой код XAML в представлении:

<Button VerticalAlignment="Top" HorizontalAlignment="Right" Margin="50" Width="50" Height="50" Background="Transparent" BorderBrush="Transparent" Content="OK" FontSize="32" FontWeight="Bold" Foreground="White" Click="{x:Bind Path=ViewModel.ButtonMainClick, Mode=OneWay}"/> 

В ViewModel у меня так:

public void ButtonMainClick()
{
    // TO DO
}

И это прекрасно работает.

Теперь я хочу вызвать некоторый метод с параметрами. Но я не могу сделать это таким образом. Я видел в Интернете, что существует EventTriggerBehavior. Но я не уверен, как его использовать и передать некоторые параметры.

Есть идеи?


person user3239349    schedule 29.10.2018    source источник
comment
Всегда ли значения параметров одинаковы или они зависят от других значений/элементов управления?   -  person Peter Torr - MSFT    schedule 29.10.2018
comment
Параметры из Enum, которые я определил.   -  person user3239349    schedule 29.10.2018
comment
Но всегда ли вы передаете одно и то же значение?   -  person Peter Torr - MSFT    schedule 29.10.2018
comment
Да, вот так: ButtonMainClickEvent?.Invoke(Model.ControlTypeEnum);   -  person user3239349    schedule 29.10.2018


Ответы (2)


Предполагая, что привязка UWP и WPF работает одинаково, вы должны привязать свои кнопки к свойствам типа ICommand в файле ViewModel. Распространенной реализацией ICommand является RelayCommand, которая может выглядеть так:

 public class RelayCommand : ICommand
    {
        private readonly Action _targetExecuteMethod;
        private readonly Func<bool> _targetCanExecuteMethod;
        public RelayCommand(Action executeMethod) => _targetExecuteMethod = executeMethod;

        public RelayCommand(Action executeMethod, Func<bool> canExecuteMethod)
        {
            _targetExecuteMethod = executeMethod;
            _targetCanExecuteMethod = canExecuteMethod;
        }
        public void RaiseCanExecuteChanged() => CanExecuteChanged(this, EventArgs.Empty);
        bool ICommand.CanExecute(object parameter) => _targetCanExecuteMethod?.Invoke() ?? _targetExecuteMethod != null;
        public event EventHandler CanExecuteChanged = delegate { };
        void ICommand.Execute(object parameter) => _targetExecuteMethod?.Invoke();
    }
    public class RelayCommand<T> : ICommand
    {
        private readonly Action<T> _targetExecuteMethod;
        private readonly Func<T, bool> _targetCanExecuteMethod;
        public RelayCommand(Action<T> executeMethod) => _targetExecuteMethod = executeMethod;
        public RelayCommand(Action<T> executeMethod, Func<T, bool> canExecuteMethod)
        {
            _targetExecuteMethod = executeMethod;
            _targetCanExecuteMethod = canExecuteMethod;
        }
        public void RaiseCanExecuteChanged() => CanExecuteChanged(this, EventArgs.Empty);
        bool ICommand.CanExecute(object parameter)
        {
            if (_targetCanExecuteMethod != null)
            {
                var tparm = (T)parameter;
                return _targetCanExecuteMethod(tparm);
            }
            return _targetExecuteMethod != null;
        }
        public event EventHandler CanExecuteChanged = delegate { };
        void ICommand.Execute(object parameter) => _targetExecuteMethod?.Invoke((T)parameter);
    }

Для View и ViewModel взгляните на этот пример: https://stackoverflow.com/a/53045098/9889260

Если вы хотите использовать параметры. Вы просто используете общий RelayCommand<T>, где T - это тип, который вы хотите передать в качестве параметра, а также даете свои методы Excecute и CanExcecute (если они у вас есть) T в качестве параметра.

С уважением, мисс

person misdirection    schedule 29.10.2018

Обработчики событий, использующие x:Bind, не могут принимать произвольные аргументы.

Одним из простых решений является предоставление другого метода, который делает вызов за вас. Метод, привязанный к событию Click, не имеет параметров, а просто вызывает метод, который необходимо выполнить.

[Изменить] Если вам нужно передать контекст из элемента управления, сгенерировавшего событие, вы можете сделать это, добавив сигнатуру события и обратившись к sender DataContext и приведя свой тип ViewModel.

void FuncThatDoesSomething(T1 arg1, T2 arg2,...)
{
  // do it
}

// function has to match the event handler signature 
void FuncThatIsBoundInXaml(object sender, RoutedEventArgs e)
{
  var vm = sender.DataContext as YourViewModelType;
  // call method with required args
  FuncThatDoesSomething(vm.SomeProperty, 42);
}
person Peter Torr - MSFT    schedule 29.10.2018
comment
Но каждый элемент в ListView, который использует это событие, имеет другое значение в этом Enum. - person user3239349; 29.10.2018
comment
Отсюда и вопрос выше о том, было ли оно постоянным или нет :-). Есть ли что-нибудь в элементе управления, который позволяет узнать, какое значение перечисления нужно передать? - person Peter Torr - MSFT; 29.10.2018
comment
Ну, у каждого элемента есть Модель, и в этой модели EnumType вычисляется на основе какого-то другого параметра. Сама модель каждого предмета имеет эту информацию. :) - person user3239349; 29.10.2018
comment
‹ItemsControl Name=lvItems ItemsSource={x:Bind Path=ViewModel.Model.ListItems, Mode=OneWay} ItemTemplate={StaticResource ItemTemplate}/› ‹DataTemplate x:Key=ItemTemplate x:DataType=data:GlobalListItem› ‹Controls:ListItemControl Model={x:Bind Mode=OneWay}/› ‹/DataTemplate› - person user3239349; 29.10.2018
comment
Была ли кнопка в вашем ListViewItem? Возможно, вы опубликуете минимально воспроизводимый пример, тогда мы сможем вам больше помочь. - person Xie Steven; 31.10.2018