it-swarm.com.ru

Как написать ViewModelBase в MVVM

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

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

ViewModelBase

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

19
DriLLFreAK100

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

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

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

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

    атрибут [CallerMemberName] не является обязательным, но он позволит вам написать: 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 является виртуальным, и вы можете расширить его в более конкретный класс, например, для запуска проверки или путем вызова события PropertyChanging.

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

чтобы получить больше возможности повторного использования и сохранить 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);
    }
}
56
Liero

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

  1. МВВМ лайт
  2. MVVM Cross
  3. Призма

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

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

4
OrcusZ

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

2
Paul Smith

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

Взгляните на исходный код для классов MVVM Light ViewModelBase и ObservableObject classes. ObservableObject - это в основном реализация INotifyPropertyChanged, использующая лямбда-выражение вместо «магических строк» ​​для имени свойства. ViewModelBase расширяет ObservableObject и в основном является служебным методом для определения того, работаете ли вы в конструкторе Visual Studio

0
Peregrine

Приведенный ниже класс можно использовать в качестве базы моделей View в проектах WPF:

public abstract class ViewModelBase : INotifyPropertyChanged
{
    // uncomment the line below for Log4Net Logging
    //private static readonly log4net.ILog Log = Logging.For<ViewModelBase>();

    /// <summary>
    /// Multicast event for property change notifications.
    /// </summary>
    public event PropertyChangedEventHandler PropertyChanged;

    /// <summary>
    /// Checks if a property already matches a desired value.  Sets the property and
    /// notifies listeners only when necessary.
    /// </summary>
    /// <typeparam name="T">Type of the property.</typeparam>
    /// <param name="storage">Reference to a property with both getter and setter.</param>
    /// <param name="value">Desired value for the property.</param>
    /// <param name="propertyName">Name of the property used to notify listeners.This
    /// value is optional and can be provided automatically when invoked from compilers that
    /// support CallerMemberName.</param>
    /// <returns>True if the value was changed, false if the existing value matched the
    /// desired value.</returns>
    protected virtual bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
    {
        if (object.Equals(storage, value)) return false;
        storage = value;
        // Log.DebugFormat("{0}.{1} = {2}", this.GetType().Name, propertyName, storage);
        this.OnPropertyChanged(propertyName);
        return true;
    }

    /// <summary>
    /// Notifies listeners that a property value has changed.
    /// </summary>
    /// <param name="propertyName">Name of the property used to notify listeners.  This
    /// value is optional and can be provided automatically when invoked from compilers
    /// that support <see cref="CallerMemberNameAttribute"/>.</param>
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        var eventHandler = this.PropertyChanged;
        if (eventHandler != null)
            eventHandler(this, new PropertyChangedEventArgs(propertyName));
    }
}

И пример ViewModel класс:

public class MyViewModel : ViewModelBase
{
    private int myProperty;
    public int MyProperty
    {
        get { return myProperty; }
        set { SetProperty(ref myProperty, value);
    }
}
0
Mohammad Reza Rastegari