В данном уроке по паттерну MVVM для приложений WPF был такой пример:
private RelayCommand addCommand;
public RelayCommand AddCommand
{
get
{
return addCommand ??
(addCommand = new RelayCommand(obj =>
{
Phone phone = new Phone();
Phones.Insert(0, phone);
SelectedPhone = phone;
}));
}
}
Это команда на добавление нового объекта, находящаяся в ApplicationViewModel. Теперь мы можем привязать кнопке эту команду:
<Button Command="{Binding AddCommand}">+</Button>
Одна из первых проблем, с которой я столкнулся при переписывании своего проекта в соответствии с шаблоном MVVM - это установка связи между командой RelayCommand и методом, где и описывается логика команды (впрочем, если это позволено и если логики мало, то можно и прямо внутри RelayCommand логику описать).
Вот конкретный неработающий пример. Метод ToogleButton должен изменяеть надпись на кнопке при нажатии на неё:
private void ToggleButton(object sender, EventArgs e) {
Button clickedButton = sender as Button;
if (clickedButton.Tag.Equals("On")) {
clickedButton.Content = "Off";
clickedButton.Tag="Off";
}
else {
clickedButton.Content = "On";
clickedButton.Tag="Off";
}
}
С событиями делалось всё просто и этот метод работал прекрасно, но в случае с MVVM событий принято избегать. Каким образом следует исправить приведённый ниже код, чтобы он заработал?
private RelayCommand clickButton;
public RelayCommand ClickButton {
ToggleButton();
}
private void ToggleButton(object sender, EventArgs e) {
Button clickedButton = sender as Button;
if (clickedButton.Tag.Equals("On")) {
clickedButton.Content = "Off";
clickedButton.Tag="Off";
}
else {
clickedButton.Content = "On";
clickedButton.Tag="Off";
}
}
XAML:
<Button Command="{Binding ClickButton}" Content="On" Tag="On" />
Я покажу как бы сделал это я.
Заведите в VM свойство для команды:
public ICommand OnCommand { get; }
Заведите свойство, которое будет отображать состояние (On/Of), например:
bool isOn;
public bool IsOn
{
get => isOn;
set
{
isOn = value;
OnPropertyChanged(nameof(IsOn));
}
}
Заведем метод, который будет переключать состояние:
void Switch()
{
IsOn = !IsOn;
// тут ваша логика
}
В конструкторе VM создадим команду:
public MainVm()
{
OnCommand = new RelayCommand(_ => Switch());
}
Мне не нравится синтаксис типа:
private RelayCommand addCommand;
public RelayCommand AddCommand
{
get
{
return addCommand ??
(addCommand = new RelayCommand(obj =>
{
Phone phone = new Phone();
Phones.Insert(0, phone);
SelectedPhone = phone;
}));
}
}
Он слишком громоздок и избыточен
Теперь View-часть:
<Button Command="{Binding OnCommand}" Content="{Binding IsOn}"/>
Это уже работает, но текст на кнопке будет переключаться между False/True, давайте напишем конвертер, который будет выводить то что нужно нам:
class BoolToOnOffConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (bool)value ? "On" : "Off";
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Привязка будет работать в одну сторону, поэтому я не реализовываю метод ConvertBack. Метод Convert получает в параметре bool-значение и возвращает строку, которую мы выведем на кнопке.
Добавим в разметку ресурс с конвертером:
<Window.Resources>
<local:BoolToOnOffConverter x:Key="BoolToOnOffConverter"/>
</Window.Resources>
Теперь воспользуемся этим конвертером:
<Button Command="{Binding OnCommand}"
Content="{Binding IsOn, Converter={StaticResource BoolToOnOffConverter}}"/>
Готово!
Ну и напоследок небольшая хитрость, позволяющая несколько разгрузить и упростить разметку.
Давайте уберем ресурс с конвертером, а сам конвертер определим как расширение разметки, нам потребуется добавить всего строчку кода, 90% которой нам сгенерирует студия:
class BoolToOnOffConverter : MarkupExtension, IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (bool)value ? "On" : "Off";
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
public override object ProvideValue(IServiceProvider serviceProvider) => this;
}
Отлично. Используем это так:
<Button Command="{Binding OnCommand}"
Content="{Binding IsOn, Converter={local:BoolToOnOffConverter}}"/>
Апостиль в Лос-Анджелесе без лишних нервов и бумажной волокиты
Основные этапы разработки сайта для стоматологической клиники
Продвижение своими сайтами как стратегия роста и независимости