Ответ
Двусторонняя привязка (TwoWay) — это основа интерактивных форм в WPF. Она обеспечивает автоматическую синхронизацию данных между свойством в UI-элементе (цель) и свойством в источнике данных (чаще всего ViewModel).
Полный рабочий пример: Форма редактирования профиля.
1. ViewModel (реализует INotifyPropertyChanged):
using System.ComponentModel;
using System.Runtime.CompilerServices;
public class UserProfileViewModel : INotifyPropertyChanged
{
private string _name;
private int _age;
private bool _isSubscribed;
public string Name
{
get => _name;
set
{
if (_name != value)
{
_name = value;
OnPropertyChanged();
// Можно добавить логику, например, валидацию:
// if (string.IsNullOrWhiteSpace(value)) throw new ArgumentException("Имя не может быть пустым");
}
}
}
public int Age
{
get => _age;
set
{
if (_age != value && value >= 0)
{
_age = value;
OnPropertyChanged();
}
}
}
public bool IsSubscribed
{
get => _isSubscribed;
set
{
if (_isSubscribed != value)
{
_isSubscribed = value;
OnPropertyChanged();
}
}
}
// Реализация INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
2. XAML-разметка (View):
<Window x:Class="ProfileEditor.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Редактор профиля" Height="250" Width="300">
<StackPanel Margin="10">
<Label>Имя:</Label>
<!-- TwoWay binding с обновлением источника при каждом изменении текста -->
<TextBox Text="{Binding Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
<Label>Возраст:</Label>
<!-- TwoWay binding для числового поля. ValidatesOnExceptions=True перехватывает исключения из сеттера -->
<TextBox Text="{Binding Age, Mode=TwoWay, UpdateSourceTrigger=LostFocus, ValidatesOnExceptions=True}"/>
<Label>Подписка на новости:</Label>
<!-- Для CheckBox IsChecked по умолчанию уже TwoWay -->
<CheckBox IsChecked="{Binding IsSubscribed}" Content="Получать уведомления"/>
<Separator Margin="0,10"/>
<!-- OneWay binding для отображения текущих данных из ViewModel -->
<TextBlock FontWeight="Bold" Text="Предпросмотр:"/>
<TextBlock Text="{Binding Name, StringFormat='Имя: {0}'}"/>
<TextBlock Text="{Binding Age, StringFormat='Возраст: {0}'}"/>
<TextBlock Text="{Binding IsSubscribed, StringFormat='Подписка активна: {0}'}"/>
</StackPanel>
</Window>
3. Установка DataContext (например, в коде окна):
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
// Создаем и присваиваем ViewModel как контекст данных для всего окна
this.DataContext = new UserProfileViewModel
{
Name = "Иван Иванов",
Age = 30,
IsSubscribed = true
};
}
}
Как это работает:
- При запуске данные из ViewModel (
Name="Иван Иванов") автоматически отображаются вTextBoxиTextBlock. - Когда пользователь меняет текст в
TextBox, сеттер свойстваNameв ViewModel вызывается немедленно (благодаряUpdateSourceTrigger=PropertyChanged). - Вызов
OnPropertyChanged()в сеттере уведомляет WPF об изменении свойства. - WPF автоматически обновляет все элементы, привязанные к этому свойству, включая
TextBlockв секции «Предпросмотр».
Важные нюансы:
UpdateSourceTrigger: ДляTextBoxчасто используютPropertyChangedдля мгновенной обратной связи, но это может быть накладно для тяжелых операций. ДляSliderилиCheckBoxобычно оставляют значение по умолчанию.- Валидация: Двусторонняя привязка позволяет легко интегрировать валидацию через
INotifyDataErrorInfo,ValidationRulesили исключения в сеттерах (ValidatesOnExceptions). - Источник данных: В реальных приложениях
DataContextобычно устанавливается через DI-контейнер (например, в MVVM-фреймворках типа Prism или MVVM Toolkit).