2015-09-30 2 views
3

Я немного путаюсь с использованием класса BindableBase и как применить этот «новый» механизм к классическому дизайну MVVM.Использование BindableBase в классическом дизайне MVVM

Вкратце, вопрос в следующем: как правильно использовать класс BindableBase, когда мы ссылаемся на модель в нашем классе модели представления?

Детали:

Классическая модель MVVM: Просмотр < -> View-модель -> Модель

Как мы видим View-модель в этой схеме знает о модели, но модель ничего не знает о View и View-модель.

Если мы реализуем этот подход, мы будем иметь что-то вроде этого: изображения

// Model 
class Task 
{...} 

// View-Model 
class TaskViewModel : BindableBase 
{ 
    private readonly Task _task; 

    public TaskViewModel(Task task) 
    { 
     _task = task; 
    } 
    ... 
} 

Давайте, что класс Task имеет свойство «Subject», и мы должны показать эти данные. Так, по MVVM я должен:

дублированию свойства «Subject» в View-модели:

// View-Model 
class TaskViewModel : BindableBase 
{ 
    public String Subject 
    { 
     get{ return _task.Subject; } 
     set 
     { 
      _task.Subject = value; 

      // I can't use SetProperty(ref _task.Subject, value) 
      // it's contradict c# syntax 
      OnPropertyChanged("Subject"); 
     } 
    } 
} 

Как вы видите, я не могу использовать метод SetProperty для такой конструкции, и единственный способ это призвание raw onPropertyChanged метод.

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

Вы знаете, как использовать BindableBase для указанного дизайна и получить некоторое улучшение кода?

Thanks

+0

Я думаю, что viewmodel ничего не знает о представлении так ... Классический шаблон MVVM: View -> View-Model -> Model – Jose

+0

Не согласен, https://en.wikipedia.org/wiki/Model_View_ViewModel View имеет ссылку на View-Model через DataContext –

+0

Ops, I typo :) Я думал о View. В «чистом» MVVM это не должно быть, но, насколько я знаю, строгого ограничения нет, и обычно у него есть ссылка, потому что это удобно. Во всяком случае, он не связан с subj. –

ответ

2

В настоящее время ваш ViewModel подвергает Model объектам недвижимости View. Это штраф, однако это становится довольно нелепо, если у вашего Model есть много свойств, которые необходимо предъявить. Представляете ли вы создавать свойства для Model, который имеет 20+ свойств?

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

public MyClass Model { get; private set; } 

Примечание: Это тоже может реализовать INotifyPropertyChanged.

И свойства в вашем Model должны реализовать INotifyPropertyChanged, или в вашем случае, BindableBase.

public class MyClass : BindableBase 

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

Единственным недостатком является то, что ваш Model теперь зависит от BindableBase, это не конец света, но если вы находитесь в ситуации, когда вы не можете изменить Model классы, то ваш нынешний подход путь.

+0

Спасибо, ситуация с использованием класса BindableBase в качестве базового класса для модели была неясной для меня. –

0

Я бы сохранил ваш сеттер для объекта Subject простым.

Сначала попробуйте сначала загрузить данные из вашей модели, а затем назначьте данные своим свойствам модели просмотра.

class TaskViewModel : BindableBase 
{ 
    LoadData() 
    { 
     Subject = GetSubject(); // Relies on model 
    } 

    string _subject = null; 
    public String Subject 
    { 
     get{ return _subject; } 
     set 
     { 
      _subject = value; 
      SetProperty(ref _task.Subject, value) 
     } 
    } 
} 
+0

Привет, кажется, в этом случае нет преимуществ, и это тот же подход, что и использование прямого метода OnPropertyChanged. Также, если свойство объекта поддерживает режим TwoWay, мы должны реализовать метод SaveData. –

0

Попробуйте:

class TaskViewModel : BindableBase 
{ 
    private readonly Task _task; 
    private string _subject; 

    public TaskViewModel(Task task) 
    { 
     _task = task; 

     Subject = _task.Subject; 
    } 

    public string Subject 
    { 
     get { return _subject; } 
     set 
     { 
      _task.Subject = value; 
      SetProperty(ref _subject, value) 
     } 
    } 
} 

UPD

я решил добавить некоторые пояснения после первого комментария.

BindableBase - это просто базовая версия класса, которая реализует интерфейс INotifiPropertyChanged. Это реализация SetProperty метода (для Prism рамок):

public abstract class BindableBase : INotifyPropertyChanged 
{ 
    // Some other members 

    public event PropertyChangedEventHandler PropertyChanged; 

    protected virtual bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null) 
    { 
     if (object.Equals((object) storage, (object) value)) 
      return false; 

     storage = value; 

     this.OnPropertyChanged(propertyName); 

     return true; 
    } 
} 

Как можно видеть, метод SetProperty если просто «синтаксический сахар» и OnPropertyChanged выполняется внутри. Поэтому на самом деле не имеет значения, вызываете ли вы метод SetProperty или OnPropertyChanged.

+0

Да, возможно обходное решение, но, похоже, гораздо проще просто вызвать исходный метод OnPropertyChanged вместо создания дополнительного поля и вызова SetProperty для него. –

+0

См. Обновление моего ответа. Я попытался объяснить кое-что. – hcp

1

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

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

public class ViewModelBase : BindableBase 
{ 
    protected bool SetProperty(
     Func<bool> isValueNew, 
     Action setValue, 
     [CallerMemberName] string propertyName = null) 
    { 
     if (isValueNew()) 
     { 
      return false; 
     } 

     setValue(); 
     OnPropertyChanged(propertyName); 
     return true; 
    } 
} 

isValueNew Func предназначен для определения того, отличается ли это значение. Затем вы можете использовать его как следующее:

public class MyViewModel : ViewModelBase 
{ 
    private readonly MyModel myModel = new MyModel(); 

    public string Name 
    { 
     get { return myModel.Name; } 
     set 
     { 
      if (SetProperty(() => myModel.Name == value,() => myModel.Name = value)) 
      { 
       // Do something here since the value was changed. 
      } 
     } 
    } 
} 

Это самый простой способ, которым я могу думать, чтобы добиться того, что вы, кажется, после того, как.

Смежные вопросы