Интерфейс ICommand. Как передать экземпляр в параметры метода?
Я использую MVVM.Есть список, в который по нажатию кнопки <Button Command="{Binding AddKernel}"/> добавляются элементы.
(View)Список:
<StackPanel Grid.Column="0" Grid.Row="1" VerticalAlignment="Top" >
<ListBox ItemsSource = "{Binding Kernels}" HorizontalAlignment="Stretch">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="5">
<Button Command="{Binding RemoveKernel}" Content="Delete" Margin="5" Width="40" Height="18" VerticalAlignment="Center" />
<TextBlock Text="{Binding Title}" Margin="5" FontSize="16" VerticalAlignment="Center" />
<CheckBox IsChecked="{Binding IsVisualizationKernel}" Margin="5" FontSize="16" VerticalAlignment="Center" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
(Model)В модели есть метод, который должен удалять элемент списка. В параметры этого метода мне нужно передать экземпляр Kernel.
public static void RemoveKernel(object kernel)
{
if (kernel is Kernel)
{
Kernels.Remove((Kernel)kernel);
}
else
{
System.Console.WriteLine("TRIWOGA!!!");
}
}
(ViewModel)Этот метод вызываю вот так:
public ICommand RemoveKernel
{
get
{
return new RelayCommand(Well.RemoveKernel);
}
}
Я не могу сообразить как передать в параметры метода RemoveKernel экземпляр Kernel?
Ответы (1 шт):
Зависит от того, какую именно реализацию класса RelayCommand вы используете. Не все реализации данного класса из тех что я видел, поддерживают передачу параметра в команду.
Допустим, что вы используете эту реализацию:
public class RelayCommand : ICommand
{
private readonly Action<object> _execute;
private readonly Func<object, bool> _canExecute;
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public RelayCommand(Action<object> execute, Func<object, bool> canExecute = null)
{
_execute = execute;
_canExecute = canExecute;
}
public bool CanExecute(object parameter) => _canExecute == null || _canExecute(parameter);
public void Execute(object parameter) => _execute(parameter);
}
Тогда передача элемента списка в команду будет выглядеть вот так
<Button Command="{Binding RemoveKernel}" CommandParameter="{Binding}" Content="Delete" Margin="5" Width="40" Height="18" VerticalAlignment="Center" />
CommandParameter={Binding} передает текущий объект, элемент списка в команду целиком.
А реализация вот так
private ICommand _removeKernel;
public ICommand RemoveKernel => _removeKernel ?? (_removeKernel = new RelayCommand(parameter =>
{
// сопоставление шаблона 'is' позволяет сразу присвоить его результат переменной
if (parameter is Kernel kernel)
Kernels.Remove(kernel);
else
Debug.WriteLine("TRIWOGA!!!");
}));
Обратите внимание на местонахождение команды. Если она находится в ViewModel окна, а не внутри класса Kernel, а судя по тому, как вы вызываете удаление элемента, это так и есть, то вам нужно биндить команду по релативному пути к Window, через его DataContext.
<Button Command="{Binding DataContext.RemoveKernel, RelativeSource={RelativeSource AncestorType=Window}}" CommandParameter={Binding} Content="Delete" Margin="5" Width="40" Height="18" VerticalAlignment="Center" />
Еще можно запретить выполнение команды, задизаблить кнопку, если объект элемента коллекции не является классом Kernel, с помощью CanExecute команды:
private ICommand _removeKernel;
public ICommand RemoveKernel => _removeKernel ?? (_removeKernel = new RelayCommand(parameter =>
{
// исключения при касте не будет, потому что это уже проверено в CanExecute
Kernels.Remove((Kernel)parameter);
}, parameter => parameter is Kernel));
Кнопка сама задизаблится, если условие CanExecute вернет false. Это очень удобно. :)