Назначение объекта другому не вызывает событие PropertyChanged в WPF

Я попытался изолировать свою проблему в очень простом (и работающем) проекте. Допустим, у меня есть простая модель с двумя полями, я настроил ее свойства и событие PropertyChanged. Это упрощенная реализация моей модели. Обратите внимание, что последний метод просто получает новый клиентский объект из списка и возвращает его:

public class Clients : INotifyPropertyChanged
{
    private long _Id = 0;

    public long Id 
    {
        get { return this._Id; } 
        set { 
            this._Id = value;
            RaisePropertyChanged("Id");
        } 
    }

    private String _Name = string.Empty;

    public String Name 
    {
        get { return this._Name; }
        set { 
            if (value is null) 
                this._Name = string.Empty;
            else
                this._Name = value;
            RaisePropertyChanged("Name");
        } 
    }

    public event PropertyChangedEventHandler? PropertyChanged;

    internal void RaisePropertyChanged(string name)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
    }

    // Let's say this is a populated list with 100 clients with different ids and names
    public static List<Clients> clients = new List<Clients>();
    // ...method for loading the list with DB data

    public static Clients? LoadClient(long id)
    {
        return clients.FirstOrDefault(x => x.Id == id);
    }
}

И у меня есть простой файл .xaml для отображения данных одного клиента (идентификатор и имя). Каждый элемент управления пользовательского интерфейса имеет привязку к свойству клиента и триггеру обновления. У меня также есть две настроенные кнопки, которые будут вносить изменения в значения клиента:

<TextBox x:Name = "txtClientId" Text = "{Binding Id, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
</TextBox>
<TextBox x:Name = "txtClientName" Text = "{Binding Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
</TextBox>
<Button Name = "btnNext" Click = "btnNext_Click">Next</Button>
<Button Name = "btnRandom" Click = "btnRandom_Click">Random</Button>

Теперь давайте посмотрим на мой .xaml.cs код. Вот инициализация и код для двух кнопок: одна из них просто изменяет свойства клиента непосредственно в объекте DataContext, а другая просто назначает новый объект клиента клиенту DataContext:

public partial class ClientsPage : Page
{
    private Models.Clients? client = null;

    public ClientsPage()
    {
        InitializeComponent();

        // Load all clients data in Models.Clients.clients list...
        client = new Models.Clients();

        DataContext = client;
    }
    
    private void btnNext_Click(object sender, RoutedEventArgs e)
    {
        client = Models.Clients.LoadClient(client.Id + 1);
    }

    private void btnRandom_Click(object sender, RoutedEventArgs e)
    {
        Random random = new Random();
        client.Id = random.Next(999);
        client.Name = "RANDOM";
    }
}
  • Когда я устанавливаю DataContext, данные отображаются правильно в пользовательском интерфейсе -> ОК.
  • Когда я вношу изменения непосредственно в свойства объекта клиента, изменения правильно отражаются в пользовательском интерфейсе -> ОК.
  • Но... когда я загружаю новый клиентский объект и присваиваю его переменной, установленной как DataContext, пользовательский интерфейс не меняется, поскольку событие PropertyChanged не вызывается.

Конечно, эту ситуацию можно решить, присвоив значения свойств вместо прямого назначения нового объекта:

private void btnNext_Click(object sender, RoutedEventArgs e)
{
    Models.Clients newClient = Models.Clients.LoadClient(client.Id + 1);

    this.client.Id = newClient.Id;
    this.client.Name = newClient.Name;
}

Но это очень утомительно в реальном проекте с множеством моделей и методов, которые могут загружать/изменять объект с большим количеством полей. Я ищу простой/масштабируемый способ создания события PropertyChanged, когда я назначаю новый объект переменной DataContext. Если вам нужна более подробная информация о моем коде или у вас есть какие-либо вопросы, я буду рад задать их.

Знаете ли вы, что можно поднять свойство, повешенное с нулевым параметром, и это эквивалентно всем свойствам?

Andy 30.03.2024 14:30

Лично я предпочитаю в первую очередь модель просмотра. Страница или пользовательский элемент управления будут созданы на основе модели представления, тогда это будет совершенно новая часть пользовательского интерфейса, когда свойство currentviewmodel будет изменено на новую модель представления. Не совсем уверен, что я следую тому, чего вы хотите достичь.

Andy 30.03.2024 14:34

Если у вас есть новый объект, назначьте его также DataContext.

Gilad Waisel 30.03.2024 14:39

Это то, что я искал @GiladWaisel, с помощью которого я могу решить все свои проблемные ситуации, используя одно задание вместо того, чтобы назначать поле за полем (у меня есть объекты с +20 полями). Спасибо, чувак, если хочешь, можешь установить это как ответ.

Windgate 30.03.2024 14:56

@Энди, раньше у меня был внутренний void RaisePropertyChanged([CallerMemberName] String name = ""), но как вы думаете, где я мог бы вызвать этот метод с нулевым параметром, чтобы решить что-то, связанное с моей проблемой? Спасибо за замечание, но я думаю, что это ничего не решит для меня.

Windgate 30.03.2024 15:00
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
5
65
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Новый экземпляр объекта создан, но не назначен DataContext. Требуется присвоение DataContext. Правильный код:

private void btnNext_Click(object sender, RoutedEventArgs e)
    {
        client = Models.Clients.LoadClient(client.Id + 1);
        DataContext = client; 
    }

Другие вопросы по теме