OnPropertyChanged обновление 2-го уровня - PullRequest
0 голосов
/ 05 июля 2018

Поиск не принес мне никаких улик, и я в некотором затруднении. Пока что WPF самоучка, так что я могу упустить что-то простое.

<Window.DataContext>
    <local:MainViewModel/>
</Window.DataContext>

<TextBlock Text={Binding BoundTextProperty}"/>

это упрощенный xml

public class MainViewModel
{
    private Model Data;
    public MainViewModel()
    {...}
    public string BoundTextProperty => Data.BoundTextProperty;
    ...
}

Свойство, которое связано со свойством, содержащим данные в модели

public class Model : INotifyPropertyChanged
{
    private long number;
    public long Number
    {
        get { return number; }
        set 
        {
            number = value;
            OnPropertyChanged(nameof(BoundTextProperty));
        }
    }

    public string BoundTextProperty => $"Some text {Number} some text again";

    public virtual event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

Клянусь, в какой-то момент это сработало. В строке есть пара других переменных, но это основной принцип того, как она работает, а точнее - нет.

Мой вопрос более или менее обязателен, и переплет может всплыть, и если да, то почему?

Ответы [ 2 ]

0 голосов
/ 05 июля 2018

Вам нужно вызвать событие PropertyChanged для свойства источника, с которым вы связываете свой XAML. В этом случае вы связываетесь с BoundTextProperty из MainViewModel, что означает, что класс MainViewModel должен вызвать событие PropertyChanged.

Неважно, содержит ли свойство source другое свойство класса, которое вызывает событие PropertyChanged. Это исходный объект привязки, который уведомляет представление.

Вы также можете просто привязать свойство "model" напрямую, при условии, что вы превратите Data в публичное свойство в вашей модели представления:

public Model Data { get; private set; }
...
<TextBlock Text="{Binding Data.BoundTextProperty}"/>

Если вы решите придерживаться своего свойства оболочки, MainViewModel должен реализовать событие INotifyPropertyChanged и вызывать событие PropertyChanged при каждом обновлении модели:

public class MainViewModel : INotifyPropertyChanged
{
    private readonly Model Data;

    public MainViewModel()
    {
        Data = new Model();
        Data.PropertyChanged += Data_PropertyChanged;
    }

    private void Data_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        OnPropertyChanged("BoundTextProperty");
    }

    public string BoundTextProperty => Data.BoundTextProperty;

    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}
0 голосов
/ 05 июля 2018

Вы должны добавить код для всплытия события PropertyChanged модели из ViewModel в View. Вот пример (на основе вашего кода):

public class MainViewModel : ViewModelBase
{
    private readonly Model Data;

    public MainViewModel()
    {
        Data = new Model();
        Data.PropertyChanged += ModelOnPropertyChanged;
    }

    private void ModelOnPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        switch (e.PropertyName)
        {
            case nameof(Model.BoundTextProperty):
                OnPropertyChanged(nameof(MainViewModel.BoundTextProperty));
                break;
            // add cases for other properties here:
        }
    }

    public string BoundTextProperty => Data.BoundTextProperty;
}

public class Model : ModelBase
{
    private long number;
    public long Number
    {
        get { return number; }
        set
        {
            number = value;
            OnPropertyChanged(nameof(BoundTextProperty));
        }
    }

    public string BoundTextProperty => $"Some text {Number} some text again";

}

public abstract class ViewModelBase : Base
{
    // add other ViewModel related stuff here
}


public abstract class ModelBase : Base
{
    // add other Model related stuff here
}

public abstract class Base : INotifyPropertyChanged
{
    public virtual event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}
...