Каждый рецепт имеет три поля: «Имя», «Категория» и «Калориен». Проект разделен на три отдельных уровня в соответствии со стандартом MVVM — Model, ViewModel и View как отдельные проекты в одном решении.
Базовая функциональность работает нормально. Рецепты загружаются при запуске, в списке они отображаются правильно, а нажатие кнопки «Добавить» создает новый рецепт и добавляет его в список. Текстовые поля ввода также очищаются после нажатия кнопки, что говорит мне о том, что уведомления PropertyChanged работают правильно для свойств ввода в VMMainWindow.
Проблема начинается, когда я выбираю элемент из списка и пытаюсь отобразить его сведения в текстовых блоках ниже. Текстовые блоки привязаны к SelectedRezept.Name, SelectedRezept.Kategorie и SelectedRezept.Kalorien.
При первом выборе они показывают правильные значения, что означает, что сам путь привязки правильный и SelectedRezept установлен правильно.
Однако при запуске метода, который изменяет одно из этих свойств выбранного VMRezept объект, текстовый блок не обновляется. Я могу подтвердить в отладчике, что базовое значение изменяется — пользовательский интерфейс просто не улавливает это.
Отображение обновляется только в том случае, если я нажимаю на другой элемент и затем повторно выбираю исходный, что означает, что WPF не уведомляется об изменении свойства.
Я заметил, что мои установщики в VMRezept вообще не вызывают PropertyChanged — они просто передают значение напрямую в модель. У меня есть вспомогательный метод OnPropertyChanged, определенный в VMRezept, но я никогда его нигде не вызываю. Я не уверен, является ли это основной причиной или реальная проблема заключается в том, что PropertyChanged в дочернем объекте модели представления не перемещается автоматически в родительский VMMainWindow, где находится привязка.
Я попробовал снова запустить PropertyChanged для SelectedRezept из VMMainWindow после вызова метода, и это приводит к обновлению текстовых блоков, но уведомлять об этом кажется неправильным что весь выбранный объект изменился, хотя на самом деле изменилось только одно свойство внутри него.
Мои вопросы:
- Должен ли каждый установщик в VMRezept вызывать PropertyChanged, даже если значения поступают из уровня модели?
- Вызывает ли вручную повторный запуск PropertyChanged для SelectedRezept из родительского элемента является стандартным подходом, или существует более чистый шаблон для этого?
- PropertyChanged в дочерней модели представления когда-либо автоматически распространяется на привязки в родительском элементе, или WPF прослушивает только объект, с которым он непосредственно связан?
Код: Выделить всё
using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ViewModel { public class VMMainWindow : INotifyPropertyChanged { public event PropertyChangedEventHandler? PropertyChanged; ObservableCollection rezepteListe; VMRezept aktRezept; private UserCommand buttonCommand; private string neuerName; public string NeuerName { get => neuerName; set { neuerName = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(NeuerName))); } } private string neueKategorie; public string NeueKategorie { get => neueKategorie; set { neueKategorie = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(NeueKategorie))); } } private int neueKalorien; public int NeueKalorien { get => neueKalorien; set { neueKalorien = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(NeueKalorien))); } } public ObservableCollection RezepteListe { get => rezepteListe; set => rezepteListe = value; } public VMRezept AktRezept { get => aktRezept; set => aktRezept = value; } public UserCommand ButtonCommand { get => buttonCommand; set => buttonCommand = value; } public VMMainWindow() { rezepteListe = new ObservableCollection(); Fuellen(); this.ButtonCommand = new UserCommand(new Action(VMMWNeuesRezept)); } private void VMMWNeuesRezept(object obj) { VMRezept neuesRezept = new VMRezept(NeuerName, NeueKategorie, NeueKalorien); rezepteListe.Add(neuesRezept); NeuerName = ""; NeueKategorie = ""; NeueKalorien = 0; } public void Fuellen() { RezepteListe.Add(new VMRezept("Pasta Aglio e Olio", "vegetarisch", 400)); RezepteListe.Add(new VMRezept("Pfannkuchen", "Dessert", 450)); RezepteListe.Add(new VMRezept("Gulasch", "Hauptgericht", 940)); } } }Код: Выделить всё