Уведомление об изменениях в иерархиях MVVM - PullRequest
15 голосов
/ 06 января 2010

Скажем, в некотором абстрактном базовом классе ViewModel у меня есть свойство простого старого:

public Size Size
{
    get { return _size; }
    set
    {
        _size = value;
        OnPropertyChanged("Size");
    }
}

Затем я создаю более конкретную ViewModel, наследующую от предыдущей, которая содержит следующее свойство:

public Rect Rectangle
{
    get { return new Rect(0, 0, _size.Width, _size.Height); }
}

Теперь в некотором классе View я привязываюсь к вышеупомянутому свойству ViewModel Rectangle. Все отлично работает, пока не поменяю размер. Когда Size изменяется, Rectangle не знает об этом, и изменение не распространяется на представление. И так как Rectangle находится в дочернем классе, я не могу просто добавить OnPropertyChanged("Rectangle") к установщику Size.

Теперь представьте, что у меня много разных свойств, таких как Rectangle, все они зависят от свойств базового класса, и что ни одно из этих изменений не распространяется. Мне нужен какой-то легкий и элегантный способ создания цепочки уведомлений об изменениях, предпочтительно такой, который не требует большого количества кода и не заставляет меня использовать свойства зависимостей.

Очевидно, здесь есть много уродливых решений - я ищу что-то чистое и умное. Мне кажется, что это будет очень распространенный сценарий, и мне кажется, что это может быть MVVM-дружественный способ сделать это.

Ответы [ 5 ]

9 голосов
/ 06 января 2010

Я недавно писал об этой проблеме. Я включаю [DependsUpon("Size")] атрибут с прямоугольником. Мне ДЕЙСТВИТЕЛЬНО нравится этот подход, потому что он сохраняет знание зависимости с помощью кода, который создает зависимость, а не наоборот.

Взгляните: http://houseofbilz.com/archive/2009/11/14/adventures-in-mvvm----dependant-properties-with-inotifypropertychanged.aspx

4 голосов
/ 06 января 2010

Я использую PropertyObserver Джоша Смита, который вы можете получить из его библиотеки Фонда MVVM по адресу http://mvvmfoundation.codeplex.com/.

Использование:

_viewmodel_observer = new PropertyObserver<OtherViewModel>(_OtherViewModel)
   .RegisterHandler(m => m.Size, m => RaisePropertyChanged(Rectangle);

Атрибут Брайана тоже хорош. В PropertyObserver мне нравится то, что я могу выполнять произвольный код; позволяя мне проверять условия, которые могут заставить меня избежать повышения или выполнять другие действия вместе.

3 голосов
/ 06 января 2010

Вы можете просто переопределить OnPropertyChanged в производной модели представления следующим образом:

protected override void OnPropertyChanged(string propertyName) {
    base.OnPropertyChanged(propertyName);
    if (propertyName == "Size") {
        base.OnPropertyChanged("Rectangle");
    }
}

Еще одна возможность ... Некоторое время назад я собрал довольно хороший базовый класс ViewModel, который поддерживает атрибуты таких свойств, как:

[DependsOn("Size")]
public Rect Rectangle {
    get { new Rect(0,0,Size.Width, Size.Height); }
}

Затем базовый класс ViewModel собирает эти DependsOnAttribute во время выполнения и в своем методе OnPropertyChanged он просто смотрит, чтобы увидеть, какие другие свойства должны быть признаны недействительными при изменении свойства.

1 голос
/ 06 января 2010

Чистым способом MVVM было бы использование Messenger механизма подписки / уведомления (как в MvvmFoundation ) Джоша Смита

Создайте где-нибудь одноэлементный объект Messenger - основной класс App всегда подходит для этого

public partial class App : Application
{
    private static Messenger _messenger;
    public static Messenger Messenger
    {
        get
        {
            if (_messenger == null)
            {
                _messenger = new Messenger();
            }
            return _messenger;
        }
    }
}

В установщике размера из базового класса уведомлять об изменениях:

public Size Size
{
    get { return _size; }
    set
    {
        _size = value;
        OnPropertyChanged("Size");

        App.Messenger.NotifyColleagues("SIZE_CHANGED");
    }
}

Теперь вы можете позволить вашему унаследованному ViewModel прослушивать эти изменения и вызывать события PropertyChanged соответствующим образом ...

public MyViewModel : MyViewModelBase
{
    public MyViewModel()
    {
        App.Messenger.Register("SIZE_CHANGED", () => OnPropertyChanged("Rectangle"));
    }
}

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

Надеюсь, это поможет:)

0 голосов
/ 06 января 2010

возможно, потому что я парень из VB, но в вашем коде Rectangle похоже, что вы обращаетесь к объявлению private _size вместо свойства Public Size, которое не будет запускать событие OnPropertyChanged для предупреждения о просмотре.

Также я могу быть вне базы, но разве Rectangle не должен быть реальным объектом, в то время как размер является свойством этого объекта? Может быть, это то, что вы делаете ... некоторые методологии C # все еще очень чужды мне.

...