Базовый класс модели представления для использования с MVVM в WPF

Автор: Педро Сампайо (Pedro Sampaio)

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

Простая модель представления

public class ViewModelBase : INotifyPropertyChanged
{
    
    #region Implementation of INotifyPropertyChanged
    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged(string propertyName)
    {
    
        OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
    }
    protected virtual void OnPropertyChanged(PropertyChangedEventArgs args)
    {
        var handler = PropertyChanged;
        if (handler != null)
            handler(this, args);
    }
    #endregion
}

Это простейший из всех возможных классов. Он реализует интерфейс INotifyPropertyChanged и предоставляет два защищенных метода для уведомления об изменении конкретного свойства. Один метод принимает строку как параметр, а второй принимает объект PropertyChangedEventArgs. Проблема этого подхода в том, что продолжается создание новых экземпляров PropertyChangedEventArgs, даже при ограниченном числе свойств в классе.

Более интеллектуальная модель представления

public abstract class ViewModelBaseWithArgCache : ViewModelBase
{
    
    private readonly Dictionary<string, PropertyChangedEventArgs> eventArgsCache;
    protected ViewModelBaseWithArgCache()
    {
    
        eventArgsCache = new Dictionary<string, PropertyChangedEventArgs>();
    }
    
    #region Overrides
    protected override void OnPropertyChanged(string propertyName)
    {
        PropertyChangedEventArgs args;
        if (!eventArgsCache.ContainsKey(propertyName))
        {
            args = new PropertyChangedEventArgs(propertyName);
            eventArgsCache.Add(propertyName, args);
        }
        else
        {
            args = eventArgsCache[propertyName];
        }
        OnPropertyChanged(args);
    }
    #endregion
}

Здесь мы переопределяем метод OnPropertyChanged(string propertyName), чтобы предоставить настраиваемую реализацию. Сначала мы инициализируем словарь того, что будем использовать в качестве кэша. Теперь при вызове OnPropertyChanged(string property) мы проверяем, имеется ли экземпляр EventArgs, кэшированный для этого свойства. Если имеется, то возвращаем его. Если нет, то создаем его, добавляем в кэш и затем возвращаем. Мы используем этот экземпляр для вызова защищенного метода OnPropertyChanged(PropertyChangedEventArgs args) класса ViewModelBase.

Объединенная модель представления

public class UnifiedViewModelBase : INotifyPropertyChanged
{
    private readonly Dictionary<string, PropertyChangedEventArgs> eventArgsCache;
    
    protected UnifiedViewModelBase()
    {
    
        eventArgsCache = new Dictionary<string, PropertyChangedEventArgs>();
    }
    
 
    #region Implementation of INotifyPropertyChanged
    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged(string propertyName)
    {
        PropertyChangedEventArgs args;
        if (!eventArgsCache.TryGetValue(propertyName, out args))
        {
            args = new PropertyChangedEventArgs(propertyName);
            eventArgsCache.Add(propertyName, args);
        }
        OnPropertyChanged(args);
    }
    protected void OnPropertyChanged(PropertyChangedEventArgs args)
    {
        var handler = PropertyChanged;
        if (handler != null)
            handler(this, args);
    }
    #endregion
}

Теперь вместо создания двухуровневой иерархии классов мы объединили их вместе в один класс UnifiedViewModelBase. При желании можно изменить имя этого класса, например на ViewModelBase.

Весь код в этой публикации можно загрузить, нажав эту ссылку.

Надеюсь, это будет полезно.