C#, WPF, Visual Studio 2022 / 2019
Нам нужно создать новую папку и новые классы:
Где создаём: папка ViewModels → добавить класс → назвать ViewModelBase.cs
Библиотека: System.ComponentModel (встроенная в .NET, не нужно устанавливать).
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace MvvmBindingDemo.ViewModels
{
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}Где создаём: папка Commands → добавить класс RelayCommand.cs.
Пространство имён: System.Windows.Input (библиотека PresentationCore уже подключена в WPF проекте).
using System;
using System.Windows.Input;
namespace MvvmBindingDemo.Commands
{
public class RelayCommand : ICommand
{
private readonly Action _execute;
private readonly Func<bool> _canExecute;
public RelayCommand(Action execute, Func<bool> canExecute = null)
{
_execute = execute;
_canExecute = canExecute;
}
public bool CanExecute(object parameter) => _canExecute == null || _canExecute();
public void Execute(object parameter) => _execute();
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
}
}Где создаём: папка ViewModels → добавить класс MainViewModel.cs.
using MvvmBindingDemo.Commands;
using System.Windows.Input;
namespace MvvmBindingDemo.ViewModels
{
public class MainViewModel : ViewModelBase
{
private string userName = "Гость";
public string UserName
{
get => userName;
set
{
userName = value;
OnPropertyChanged();
}
}
public ICommand SayHelloCommand { get; }
public MainViewModel()
{
SayHelloCommand = new RelayCommand(() =>
{
UserName = "Привет, мир!";
});
}
}
}Открываем MainWindow.xaml.cs (code-behind). Находим конструктор:
public MainWindow()
{
InitializeComponent();
DataContext = new MainViewModel(); // Это самая важная строка!
}Теперь все биндинги в XAML будут искать данные в MainViewModel.
Открываем MainWindow.xaml. Внутри тега <Grid> добавляем:
<StackPanel Margin="20">
<TextBox Text="{Binding UserName, UpdateSourceTrigger=PropertyChanged}"
Margin="0,0,0,10"/>
<TextBlock Text="{Binding UserName}" FontSize="16" FontWeight="Bold"/>
<Button Content="Сказать привет" Command="{Binding SayHelloCommand}"
Margin="0,10,0,0" Padding="10"/>
</StackPanel>Объяснение:
- TextBox привязан к UserName (двусторонне).
- TextBlock показывает то же самое.
- Button вызывает команду SayHelloCommand.
Что должно произойти:
Без вызова OnPropertyChanged() UI не узнал бы об изменении свойства. Механизм:
UserName в коде (или через команду).OnPropertyChanged().PropertyChanged.set {
userName = value;
OnPropertyChanged(); // без этого UI не обновится!
}System.ComponentModel (встроено)System.Windows.Input (PresentationCore)System.Collections.ObjectModelДобавим в MainViewModel свойство-коллекцию:
using System.Collections.ObjectModel;
public ObservableCollection<string> Items { get; set; }
= new ObservableCollection<string>();
public ICommand AddItemCommand { get; }
// в конструкторе:
AddItemCommand = new RelayCommand(() => Items.Add($"Элемент {Items.Count+1}"));В XAML добавляем <ListBox ItemsSource="{Binding Items}" /> и кнопку с командой AddItemCommand.
Mode=OneWay — только из ViewModel в UI (TextBlock, изображения).Mode=TwoWay — и туда, и обратно (TextBox, CheckBox).Mode=OneTime — только один раз при загрузке.<TextBlock Text="{Binding UserName, Mode=OneWay}" />
<TextBox Text="{Binding Age, Mode=TwoWay}" />LostFocus — при потере фокуса (по умолчанию).PropertyChanged — при каждом вводе символа.<TextBox Text="{Binding SearchText, UpdateSourceTrigger=PropertyChanged}" />Добавляем новый класс BoolToVisibilityConverter.cs в папку, скажем, Converters.
using System;
using System.Globalization;
using System.Windows.Data;
namespace MvvmBindingDemo.Converters
{
public class BoolToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (bool)value ? System.Windows.Visibility.Visible : System.Windows.Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return value is System.Windows.Visibility visibility && visibility == System.Windows.Visibility.Visible;
}
}
}xmlns:conv="clr-namespace:MvvmBindingDemo.Converters"<Window.Resources>
<conv:BoolToVisibilityConverter x:Key="BoolToVis"/>
</Window.Resources>
<Button Visibility="{Binding IsAdmin, Converter={StaticResource BoolToVis}}" .../>Теперь если свойство IsAdmin в ViewModel равно true — кнопка видна, иначе скрыта.
Используем CommandParameter и сигнатуру с параметром в RelayCommand.
// RelayCommand с параметром типа T
public RelayCommand(Action<object> execute) ...
// в ViewModel:
public ICommand DeleteCommand { get; }
DeleteCommand = new RelayCommand(param => Items.Remove(param.ToString()));
// XAML:
<Button Command="{Binding DeleteCommand}" CommandParameter="{Binding ElementName=listBox, Path=SelectedItem}" />{Binding ...}.Лекция окончена. Задавайте вопросы. Спасибо за внимание!