Кнопка поддерживает команды, и вместо обработки события Click мы можем прикрепить к ней команду. Но что делать, если элемент не поддерживает команду, а нам надо обработать какое-то его действие.
Например, у нас определен элемент ListBox, и мы хотим отслеживать выбор объекта в списке. Если бы мы использовали событийную модель, то
мы бы обрабатывали событие SelectionChanged
. Но мы знаем, что выделение объекта в списке ведет к изменению свойства SelectedItem
элемента ListBox. Поэтому вместо применения события мы можем просто обрабатывать изменения свойства SelectedPhone в ApplicationViewModel, которое привязано
к свойству SelectedItem
у ListBox.
Аналогично у элемента TextBox есть событие TextChanged
, которое вызывается при изменении вводимого текста. Но опять же изменение текста приводит к изменению свойства Text
у элемента TextBox. Поэтому мы могли бы установить привязку для этого элемента к свойству Text:
<TextBox Text="{Binding Title, UpdateSourceTrigger=PropertyChanged}" />
А в классе Phone (либо в специальной ViewModel, которая инкапсулирует объект Phone) обрабатывать изменение свойства Title, которое бы синхронно менялось при вводе новых символов в текстовое поле:
public string Title { get { return phone.Title; } set { // обработка изменения свойства phone.Title = value; OnPropertyChanged("Title"); } }
В тоже время подобные действия можно установить не для всех событий. И если мы хотим связать события с командами, необходимо использовать дополнительный функционал.
В частности, нам надо добавить в проект через пакетный менеджер Nuget специальный пакет Microsoft.Xaml.Behaviors.Wpf:
Пусть у нас в проекте есть следующая модель Phone:
using System.ComponentModel; using System.Runtime.CompilerServices; namespace HelloApp { public class Phone : INotifyPropertyChanged { private string title; private string company; private int price; public Phone(string title, string company, int price) { this.title = title; this.company = company; this.price = price; } public string Title { get { return title; } set { title = value; OnPropertyChanged("Title"); } } public string Company { get { return company; } set { company = value; OnPropertyChanged("Company"); } } public int Price { get { return price; } set { price = value; OnPropertyChanged("Price"); } } public event PropertyChangedEventHandler? PropertyChanged; public void OnPropertyChanged([CallerMemberName] string prop = "") { if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(prop)); } } }
Затем определим следующий класс ApplicationViewModel, который будет представлять ViewModel:
using System; using System.Collections.ObjectModel; using System.ComponentModel; using System.Runtime.CompilerServices; using System.Windows.Input; namespace HelloApp { public class ApplicationViewModel : INotifyPropertyChanged { Phone? selectedPhone; public ObservableCollection<Phone> Phones { get; set; } // команда добавления нового объекта RelayCommand? addCommand; public RelayCommand AddCommand { get { return addCommand ?? (addCommand = new RelayCommand(obj => { Phone phone = new Phone("", "", 0); Phones.Insert(0, phone); SelectedPhone = phone; })); } } // команда удаления RelayCommand? removeCommand; public RelayCommand RemoveCommand { get { return removeCommand ?? (removeCommand = new RelayCommand(obj => { Phone? phone = obj as Phone; if (phone != null) { Phones.Remove(phone); } }, (obj) => Phones.Count > 0)); } } RelayCommand? doubleCommand; public RelayCommand DoubleCommand { get { return doubleCommand ?? (doubleCommand = new RelayCommand(obj => { Phone? phone = obj as Phone; if (phone != null) { Phone phoneCopy = new Phone(phone.Title, phone.Company, phone.Price); Phones.Insert(0, phoneCopy); } })); } } public Phone? SelectedPhone { get { return selectedPhone; } set { selectedPhone = value; OnPropertyChanged("SelectedPhone"); } } public ApplicationViewModel() { Phones = new ObservableCollection<Phone> { new Phone("iPhone 7", "Apple", 56000), new Phone("Galaxy S7 Edge", "Samsung", 60000), new Phone("Elite x3", "HP", 56000), new Phone("Mi5S", "Xiaomi", 35000) }; } public event PropertyChangedEventHandler? PropertyChanged; public void OnPropertyChanged([CallerMemberName] string prop = "") { if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(prop)); } } public class RelayCommand : ICommand { Action<object?> execute; 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) { this.execute = execute; this.canExecute = canExecute; } public bool CanExecute(object? parameter) { return canExecute == null || canExecute(parameter); } public void Execute(object? parameter) { execute(parameter); } } }
Среди прочего здесь определена команда DoubleCommand, которая добавляет копию объекта в список.
Далее добавим в представление кнопку, которая будет вызывать данную команду. Но, допустим, мы хотим выполнять эту команду, если по кнопке был совершен
двойной щелчок, то есть если произошло событие MouseDoubleClick
:
<Window x:Class="HelloApp.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:i="http://schemas.microsoft.com/xaml/behaviors" mc:Ignorable="d" Title="MainWindow" Height="350" Width="525"> <Window.Resources> <Style TargetType="TextBlock"> <Setter Property="FontSize" Value="14" /> </Style> <Style TargetType="TextBox"> <Setter Property="FontSize" Value="14" /> </Style> <Style TargetType="Button"> <Setter Property="Width" Value="40" /> <Setter Property="Margin" Value="5" /> </Style> </Window.Resources> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="*" /> <ColumnDefinition Width="0.8*" /> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="*" /> <RowDefinition Height="0.2*" /> </Grid.RowDefinitions> <ListBox Grid.Column="0" ItemsSource="{Binding Phones}" SelectedItem="{Binding SelectedPhone}"> <ListBox.ItemTemplate> <DataTemplate> <StackPanel Margin="5"> <TextBlock FontSize="18" Text="{Binding Path=Title}" /> <TextBlock Text="{Binding Path=Company}" /> <TextBlock Text="{Binding Path=Price}" /> </StackPanel> </DataTemplate> </ListBox.ItemTemplate> </ListBox> <StackPanel Grid.Row="1" Orientation="Horizontal"> <Button Command="{Binding AddCommand}">+</Button> <Button Command="{Binding RemoveCommand}" CommandParameter="{Binding SelectedPhone}">-</Button> <Button Content="2x"> <i:Interaction.Triggers> <i:EventTrigger EventName="MouseDoubleClick"> <i:InvokeCommandAction Command="{Binding DoubleCommand}" CommandParameter="{Binding SelectedPhone}" /> </i:EventTrigger> </i:Interaction.Triggers> </Button> </StackPanel> <StackPanel Grid.Column="1" DataContext="{Binding SelectedPhone}"> <TextBlock Text="Выбранный элемент" /> <TextBlock Text="Модель" /> <TextBox Text="{Binding Title, UpdateSourceTrigger=PropertyChanged}" /> <TextBlock Text="Производитель" /> <TextBox Text="{Binding Company, UpdateSourceTrigger=PropertyChanged}" /> <TextBlock Text="Цена" /> <TextBox Text="{Binding Price, UpdateSourceTrigger=PropertyChanged}" /> </StackPanel> </Grid> </Window>
Для добавленной кнопки устанавливается свойство Interaction.Triggers
, которое позволяет связать триггеры событий с командами и передать этим командам
параметры:
<Button Content="2x"> <i:Interaction.Triggers> <i:EventTrigger EventName="MouseDoubleClick"> <i:InvokeCommandAction Command="{Binding DoubleCommand}" CommandParameter="{Binding SelectedPhone}" /> </i:EventTrigger> </i:Interaction.Triggers> </Button>
В файле связанного кода MainPage.xaml.cs определим привязку ApplicationViewModel к контексту страницы MainPage
using System.Windows; namespace HelloApp { public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); DataContext = new ApplicationViewModel(); } } }
Теперь по двойному нажатию на эту кнопку произойдет дублирование в списке выделенного элемента: