Привязка данных в WPF не обновляет UI — забыл INotifyPropertyChanged?
Сделал привязку {Binding UserName} к TextBlock, в коде меняю свойство UserName, но текст на экране не меняется. При первом запуске значение показывается правильно, а дальше — глухо. Где ошибка?
public class UserVm
{
public string UserName { get; set; }
}
<TextBlock Text="{Binding UserName}"/>
3 ответа
Да, вы угадали — проблема в отсутствии INotifyPropertyChanged. Без него Binding читает значение один раз при загрузке и больше не знает, что свойство изменилось. Нужно, чтобы объект-источник «кричал» об изменениях через событие PropertyChanged.
Исправленная ViewModel:
using System.ComponentModel;
using System.Runtime.CompilerServices;
public class UserVm : INotifyPropertyChanged
{
private string _userName = "";
public string UserName
{
get => _userName;
set
{
if (_userName == value) return;
_userName = value;
OnPropertyChanged(); // уведомляем UI
}
}
public event PropertyChangedEventHandler? PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string? name = null)
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
Ключевые моменты:
- Класс реализует
INotifyPropertyChanged; - В сеттере вызывается
OnPropertyChanged(); - Атрибут
[CallerMemberName]сам подставит имя свойства ("UserName"), не нужно писать строку руками — и нельзя ошибиться в названии.
XAML менять не нужно. Теперь любое присваивание vm.UserName = "..." мгновенно обновит TextBlock.
Частые грабли рядом:
- если имя свойства в
PropertyChangedEventArgsнаписать строкой и опечататься — UI молча не обновится; - для двусторонней привязки
TextBoxдобавьтеMode=TwoWay(дляTextон и так по умолчанию) и при необходимостиUpdateSourceTrigger=PropertyChanged.
Чтобы не плодить однотипный код в каждом свойстве, посмотрите в сторону CommunityToolkit.Mvvm — там есть генератор исходного кода. Пишете так:
public partial class UserVm : ObservableObject
{
[ObservableProperty]
private string _userName = "";
}
И атрибут [ObservableProperty] сам сгенерирует публичное свойство UserName с вызовом уведомления. Меньше шаблонного кода, меньше шансов забыть OnPropertyChanged.
Ещё одна частая причина «не обновляется», даже когда INotifyPropertyChanged уже есть: вы заменяете весь объект-источник или не на ту коллекцию привязались. Если меняется список — обычный List<T> не уведомляет о добавлении/удалении элементов, нужен ObservableCollection<T>. Но в вашем конкретном случае с одним строковым свойством виновник именно отсутствие INotifyPropertyChanged.