Жирные модели, тощие ViewModels и немые взгляды, лучший подход MVVM?

18

Благодаря щедрой помощи по этому вопросу я собрал следующую структуру MVVM, которая отображает изменения модели в реальном времени в XAML (текущая дата / время), очень приятно.

  

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

В процессе работы с этим я был удивлен, увидев большую часть массового перемещения из моей модели ViewModel в мою модель , включая реализацию INotifyPropertyChange. Другое изменение заключается в том, что I больше не привязывается к свойствам в средствах ViewModel, а .

Так что в настоящее время это мой любимый вкус MVVM:

  1. Вид немой:

    • один ObjectDataProvider для каждого объекта, который вам нужен из вашей модели.
    • каждый ObjectDataProvider сопоставляет метод в ViewModel (не свойство)
    • no x: свойства имени в элементах XAML
  2. ViewModel тощий:

    • единственное, что есть в вашей модели ViewModel - это методы , с которыми связано ваше представление
  3. Модель - это жир:

    • модель реализует INotifyPropertyChanged для каждого из своих свойств.
    • для каждого метода в вашей модели ViewModel (например, GetCurrentCustomer) существует соответствующий метод singleton в вашей модели (например, GetCurrentCustomer).
    • модель заботится о любых функциях потоковой обработки в реальном времени, как в этом примере

Вопросы:

  1. Те из вас, кто реализовывал MVVM в реальных сценариях, - это основная структура, на которую вы также остановились, а если нет, то как вы меняетесь?
  2. Как бы вы это расширили, чтобы включить маршрутизированные команды и маршрутизированные события?

Следующий код будет работать, если вы просто скопируете XAML и код в новый проект WPF.

XAML:

<Window x:Class="TestBinding99382.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:TestBinding99382"
    Title="Window1" Height="300" Width="300">

    <Window.Resources>
        <ObjectDataProvider 
             x:Key="DataSourceCustomer" 
             ObjectType="{x:Type local:ShowCustomerViewModel}" 
                        MethodName="GetCurrentCustomer"/>
    </Window.Resources>

    <DockPanel DataContext="{StaticResource DataSourceCustomer}">
        <StackPanel DockPanel.Dock="Top" Orientation="Horizontal">
            <TextBlock Text="{Binding Path=FirstName}"/>
            <TextBlock Text=" "/>
            <TextBlock Text="{Binding Path=LastName}"/>
        </StackPanel>
        <StackPanel DockPanel.Dock="Top" Orientation="Horizontal">
            <TextBlock Text="{Binding Path=TimeOfMostRecentActivity}"/>
        </StackPanel>

    </DockPanel>
</Window>

Код за:

using System.Windows;
using System.ComponentModel;
using System;
using System.Threading;

namespace TestBinding99382
{
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
        }
    }

    //view model
    public class ShowCustomerViewModel
    {
        public Customer GetCurrentCustomer() {
            return Customer.GetCurrentCustomer();
        }
    }

    //model
    public class Customer : INotifyPropertyChanged
    {
        private string _firstName;
        private string _lastName;
        private DateTime _timeOfMostRecentActivity;
        private static Customer _currentCustomer;
        private Timer _timer;

        public string FirstName
        {
            get
            {
                return _firstName;
            }
            set
            {
                _firstName = value;
                this.RaisePropertyChanged("FirstName");
            }
        }

        public string LastName
        {
            get
            {
                return _lastName;
            }
            set
            {
                _lastName = value;
                this.RaisePropertyChanged("LastName");
            }
        }

        public DateTime TimeOfMostRecentActivity
        {
            get
            {
                return _timeOfMostRecentActivity;
            }
            set
            {
                _timeOfMostRecentActivity = value;
                this.RaisePropertyChanged("TimeOfMostRecentActivity");
            }
        }

        public Customer()
        {
            _timer = new Timer(UpdateDateTime, null, 0, 1000);
        }

        private void UpdateDateTime(object state)
        {
            TimeOfMostRecentActivity = DateTime.Now;
        }

        public static Customer GetCurrentCustomer()
        {
            if (_currentCustomer == null)
            {
                _currentCustomer = new Customer 
                     {  FirstName = "Jim"
                        , LastName = "Smith"
                        , TimeOfMostRecentActivity = DateTime.Now 
                     };
            }
            return _currentCustomer;
        }

        //INotifyPropertyChanged implementation
        public event PropertyChangedEventHandler PropertyChanged;
        private void RaisePropertyChanged(string property)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(property));
            }
        }
    }
}
    
задан Edward Tanguay 12.05.2009 в 14:15
источник
  • Что касается RelayCommands и RoutedCommands, вы можете посмотреть на эти ответы: stackoverflow.com/questions/650010/... –  Marc 18.04.2013 в 09:36

2 ответа

28

Вот мое мнение, для чего это стоит:

Я не согласен с предложенным вами подходом (за исключением немого представления). В реальной жизни вам часто придется использовать существующую модель: это может быть устаревший код, в котором у вас нет времени (или будет) для изменения или даже библиотеки, для которой у вас нет кода. На мой взгляд, модель должна полностью не знать, как она будет отображаться, и ее следует легко использовать в приложении, отличном от WPF. Таким образом, ему не нужно реализовывать какой-либо конкретный интерфейс, например INotifyPropertyChanged INotifyCollectionChanged , чтобы использовать его в MVVM. Я думаю, что вся логика, связанная с пользовательским интерфейсом, должна находиться в ViewModel.

Что касается RoutedEvents и RoutedCommands , они не подходят для использования с шаблоном MVVM. Обычно я стараюсь использовать как можно меньше RoutedEvents , а не RoutedCommands . Вместо этого мои ViewModels предоставляют RelayCommand свойства, которые я связываю с пользовательским интерфейсом в XAML (см. эту статью от Джоша Смита за подробные сведения о RelayCommand ). Когда мне действительно нужно обрабатывать события для какого-либо элемента управления, я использую прикрепленные поведения для сопоставления событий с командами ViewModel (посмотрите Реализация Марлона Греча )

Итак, вкратце:

  • Dumb View
  • Большой и умный ViewModel
  • Любая модель, которую вы хотите или должны использовать

Конечно, это только мой подход, и это может быть не самое лучшее, но я чувствую себя вполне комфортно с ним;)

    
ответ дан Thomas Levesque 12.05.2009 в 22:54
источник
  • Очень проницательный, особенно в отношении модели, что вы должны иметь возможность использовать шаблон MVVM для подключения к классам данных из старых проектов, которые не знают WPF. Интересно, что вы думаете, что RoutedEvents и RoutedCommands действительно не подходят для MVVM, я думал, что они были использованы для развязывания шаблонов, таких как MVVM. Спасибо за ответ. –  Edward Tanguay 13.05.2009 в 13:00
  • Я реорганизовал этот пример на основе этой обратной связи, поставил логику в ViewModel: stackoverflow.com/questions/857820/... –  Edward Tanguay 13.05.2009 в 15:30
  • Вы попали в точку ... –  Marc 18.04.2013 в 09:36
2

Я согласен с Томасом. Мой совет для любого из WPF-архитектуры:

  • Обычные объекты POCO без INotifyPropertyChange, отслеживание состояния, BL и т. д.
  • Простые и маленькие ViewModels, которые уведомляют Views точно в срок
  • Простой многоразовый пользовательский интерфейс с интеллектуальной навигационной системой, которая позволяет избежать сложных иерархий данных и сложных базовых ViewModels
  • MVVM с View Первый подход для упрощения сохранения зависимостей
  • Операции Async с задачами или Rx
  • Простая тема
  • Отсутствие сложного надежного пользовательского интерфейса, простота использования, просто использование состава и возможностей интерфейса WPFs.
  • Не стесняйтесь использовать код для динамического создания контента (формы, списки и т. д.) и сэкономить значительное время на декларативной настройке глаз (применимо к большинству случаев) - и для меня обязательно в 2015 году. Используйте методы расширения для создания Fluent API для этого.
ответ дан Panos Roditakis 08.03.2015 в 21:12
источник