Уведомление об изменении в иерархиях MVVM

Скажем, в каком-то абстрактном базовом классе ViewModel у меня есть свойство plain-old следующим образом:

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.

Ответы

Ответ 1

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

Посмотрите: http://houseofbilz.com/archive/2009/11/14/adventures-in-mvvm----dependant-properties-with-inotifypropertychanged.aspx

Ответ 2

Я использую Josh Smith PropertyObserver, который вы можете получить из его библиотеки Foundation MVVM в http://mvvmfoundation.codeplex.com/.

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

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

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

Ответ 3

Вы можете просто переопределить OnPropertyChanged в производном ViewModel, например:

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 собирает эти атрибуты DependOnAttribute во время выполнения и в методе OnPropertyChanged, он в основном просто ищет, какие другие свойства должны быть аннулированы при изменении свойства.

Ответ 4

Чистым способом MVVM было бы использовать механизм Messenger (например, в Josh Smith MvvmFoundation)

Создайте объект Singleton Messenger где-то - основной класс приложения всегда является хорошим местом для этого

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"));
    }
}

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

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

Ответ 5

возможно, потому что im vB guy, но в вашем коде Rectangle похоже, что вы получаете доступ к приватной декларации _size вместо свойства Public Size, которая не запускает событие OnPropertyChanged для предупреждения о представлении.

Также я могу быть вне базы, но не должен Rectangle быть фактическим объектом, а Size является свойством этого объекта? Возможно, это то, что вы делаете. Некоторые методологии С# по-прежнему очень чужды мне.