Как написать "ViewModelBase" в MVVM (WPF)

Я довольно новичок в среде программирования WPF. Я пытаюсь написать программу, используя шаблон проектирования MVVM.

Я сделал некоторые исследования и прочитал некоторые статьи, связанные с ним, и много раз я сталкивался с этой вещью под названием

ViewModelBase

Я знаю, что это такое.. Но могу ли я знать конкретно где я должен начать С уметь писать свою ViewModelBase? Как... Действительно понимая, что происходит без особых сложностей. Спасибо :)

5 ответов


ничего не стоит использовать фреймворки MVVM, если вы не знаете, что происходит внутри.

Итак, давайте шаг за шагом и построить свой собственный класс ViewModelBase.

  1. ViewModelBase является общим классом для всех ваших viewmodels. Давайте перенесем всю общую логику в этот класс.

  2. ваши ViewModels должны реализовать INotifyPropertyChanged (вы понимаете, почему?)

    public abstract class ViewModelBase : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
    
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
    

    the не требуется, но это позволит вам писать: OnPropertyChanged(); вместо OnPropertyChanged("SomeProperty");, поэтому вы будете избегать Строковой константы в своем коде. Пример:

    public string FirstName
    {
        set
        {
            _firtName = value;
            OnPropertyChanged(); //instead of OnPropertyChanged("FirstName") or OnPropertyChanged(nameof(FirstName))
        }
        get{ return _firstName;}
    }
    

    Пожалуйста, обратите внимание, что OnPropertyChanged(() => SomeProperty) - это больше не рекомендуется, так как у нас nameof оператор в C# 6.

  3. это обычная практика для реализации свойств, которые вызывает PropertyChanged следующим образом:

    public string FirstName
    {
        get { return _firstName; }
        set { SetProperty(ref _firstName, value); }
    }
    

    давайте определим SetProperty в вашей viewmodelbase:

    protected virtual bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = "")
    {
        if (EqualityComparer<T>.Default.Equals(storage, value))
            return false;
        storage = value;
        this.OnPropertyChanged(propertyName);
        return true;
    }
    

    он просто стреляет PropertyChanged событие когда значение свойства изменяется и возвращает true. он не запускает событие, когда значение не изменилось, и возвращает false. Основная идея в том, что SetProperty метод является виртуальным, и вы можете расширить его в более конкретном классе, e.g для запуска проверки или вызова PropertyChanging событие.

это красиво. Это все, что должна содержать ViewModelBase на данном этапе. Остальное зависит от вашего проекта. Например, ваше приложение использует базовую навигацию по страницам, и у вас есть написал свой собственный NavigationService для обработки навигации из ViewModel. Таким образом, Вы можете добавить свойство NavigationSerivce в класс ViewModelBase, чтобы иметь доступ ко всем вашим viewmodels, если хотите.

чтобы получить больше возможности повторного использования и сохранить SRP, у меня есть класс под названием bindablebase, доступного что в значительной степени является реализацией INotifyPropertyChanged, как мы сделали здесь. Я повторно использую этот класс в каждом решении WPF/UWP/Silverligt/WindowsPhone, потому что это универсально.

затем я в каждом проекте создаю пользовательский класс ViewModelBase, производный от BindableBase:

public abstract ViewModelBase : BindableBase
{
    //project specific logic for all viewmodels. 
    //E.g in this project I want to use EventAggregator heavily:
    public virtual IEventAggregator () => ServiceLocator.GetInstance<IEventAggregator>()   
}

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

public abstract PageViewModelBase : ViewModelBase
{
    //for example all my pages has title:
    public string Title {get; private set;}
}

у меня может быть другой класс для диалогов:

public abstract DialogViewModelBase : ViewModelBase
{
    private bool? _dialogResult;

    public event EventHandler Closing;

    public string Title {get; private set;}
    public ObservableCollection<DialogButton> DialogButtons { get; }

    public bool? DialogResult
    {
        get { return _dialogResult; }
        set { SetProperty(ref _dialogResult, value); }
    }

    public void Close()
    {
        Closing?.Invoke(this, EventArgs.Empty);
    }
}

У вас есть пакет nuget для реализации MVVM

  1. MVVM свет
  2. MVVM крест
  3. Присм

для меня легче для новичка MVVM light, потому что он предоставляет некоторый образец кода.

поэтому лучше установить этот пакет nuget, посмотреть о сгенерированном коде и вернуться к нам для получения дополнительных объяснений, если вам нужно.


Мне нравится это BaseVewModel это дает хороший чистый стиль в вашей модели представления. Проверьте различные сравнения " до " и "после". Конечно, ничего не является обязательным - если вам не нравится функция, которую предоставляет BaseViewModel, не используйте ее. Или измените его, потому что у вас есть исходный код. В частности, обратите внимание, что существует три разных способа реализации свойств с уведомлением об изменении-выберите уровень сложности, который вы понимаете/чувствуете себя комфортно с.


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

взгляните на исходный код для MVVM Light's ViewModelBase и ObservableObject классы. ObservableObject-это в основном реализация INotifyPropertyChanged-использование лямбда-выражения, а не" волшебных строк " для имени свойства. ViewModelBase расширяется ObservableObject и в основном является служебным методом, чтобы определить, работаете ли вы внутри Visual Studio designer


здесь есть хорошая дискуссия:https://codereview.stackexchange.com/q/13823 на эту тему. Использует хороший подход с использованием выражений, чтобы получить безопасность типов при вызове уведомлений об изменении свойства.