Я все еще не получаю MVVM! - PullRequest
8 голосов
/ 24 февраля 2010

Возможно, я слишком долго занимался разработкой Flex с такими фреймворками, как Cairngorm, но я все еще не получаю MVVM. Я знаю, что Cairngorm - это фреймворк, а MVVM - это шаблон проектирования, но здесь я сравниваю реализации Cairngorms шаблонов проектирования, главным образом контроллера представления модели и шаблона команд. Не поймите меня неправильно, я думаю, что идея связать представление с моделью представления велика, а преимущества в тестируемости и рабочем процессе дизайнера-программиста велики. Но меня беспокоят две вещи: одна - программирование всех моих действий с помощью команд, что, кстати, также ограждает меня от Cairngorm. Только в Cairngorm способ, которым они реализовали шаблон команд, дал вам преимущество наличия централизованного контроллера для всех ваших команд, который вы, похоже, не получите с MVVM, если я что-то не упустил. И если я думал, что реализация команд в Cairngorm была запутанной в MVVM, это намного хуже, я имею в виду необходимость создавать частные классы, которые реализуют ICommand для всего, что я делаю, кажется слишком много. И тогда у вас возникает проблема, что не все элементы управления реализуют команды, например, если вы используете ListBox, который я часто использую, вам не повезло; Есть обходные пути, но все они запутаны.

Другая вещь, которая беспокоит меня, это связь между View Models. В стандартном контроллере модельного представления вы собираете всю свою информацию о централизованной модели, которая наблюдается представлениями, но, похоже, это не относится к MVVM, по крайней мере, в примерах, которые я видел. Так, например, если у вас есть элемент управления со списком, который вы используете для выбора элемента, который затем используется в качестве источника для различных представлений и последующих действий, мне неясно, как вы уведомите всех об изменениях без централизованной модели.

Я знаю о MVVMFoundation и работе Тома Эршамама о командах WPF Everywhere. Меня назвали старомодным, но я думаю, что для того, чтобы по-настоящему понять шаблон, нужно создать приложение, которое использует его с нуля. Это то, что я делаю, но все время думаю, что, должно быть, упускаю что-то важное, потому что мне кажется, что я не могу успокоить этот маленький голос в моей голове, который постоянно говорит мне, что должен быть лучший способ.

Ответы [ 5 ]

3 голосов
/ 24 февраля 2010

Независимо от структуры / архитектуры / шаблона, вам всегда нужно что-то, что реагирует на нажатие кнопки, на панели инструментов / меню или в простой форме.И вам нужно что-то, что говорит, если кнопка / меню должны быть включены.Так что интерфейс ICommand хорош для этого.Я согласен с Петой, вам не нужен новый класс.Я написал простую реализацию, которая принимает 1 или 2 делегата, один для фактического ответа на щелчок (метод Execute) и необязательный для «включенного» состояния команды.Таким образом, ViewModel не загромождается.

Но я согласен, что это не централизованное хранилище команд.Но ты действительно хочешь?Я предпочитаю, чтобы команды, относящиеся к одной части приложения, находились в соответствующей модели представления, с соответствующими событиями, возникающими, когда остальная часть приложения должна быть уведомлена.свойство на ViewModel.С INotifyPropertyChanged любая часть вашего кода может реагировать на изменение.

Хороший вопрос - общение между ViewModels.Если вам нужны разные виды на одном экране, у вас может быть «супер» модель вида, которая содержит модель вида каждого вида.Существует немало фреймворков MVVM.Я использовал части помощников Марка Смита , которые довольно легки и полезны.

3 голосов
/ 24 февраля 2010

Что ж, при написании новой команды, которая реализует ICommand, кажется, что она слишком сложная, взгляните на этот класс: VB.NET: Открытый класс RelayCommand Реализует ICommand

#Region " Declarations"
    Private mCanExecute As Predicate(Of Object)
    Private mExecute As Action(Of Object)
#End Region

#Region " Constructors"
    Public Sub New(ByVal canExecute As Predicate(Of Object), ByVal execute As Action(Of Object))
        mCanExecute = canExecute
        mExecute = execute
    End Sub

    Public Sub New(ByVal execute As Action(Of Object))
        mCanExecute = Nothing
        mExecute = execute
    End Sub
#End Region

#Region " Events"
    Public Custom Event CanExecuteChanged As EventHandler Implements System.Windows.Input.ICommand.CanExecuteChanged
        AddHandler(ByVal value As EventHandler)
            AddHandler CommandManager.RequerySuggested, value
        End AddHandler
        RemoveHandler(ByVal value As EventHandler)
            RemoveHandler CommandManager.RequerySuggested, value
        End RemoveHandler
        RaiseEvent(ByVal sender As Object, ByVal e As System.EventArgs)
            Throw New ApplicationException("Can't raise custom command!")
        End RaiseEvent
    End Event
#End Region

#Region " Public Methods"
    Public Function CanExecute(ByVal parameter As Object) As Boolean Implements System.Windows.Input.ICommand.CanExecute
        If (mCanExecute Is Nothing) Then
            Return True
        End If
        Return mCanExecute(parameter)
    End Function

    Public Sub Execute(ByVal parameter As Object) Implements System.Windows.Input.ICommand.Execute
        mExecute(parameter)
    End Sub
#End Region

End Class

C #

public class RelayCommand : ICommand
{

    #region Declarations
    private Predicate<object> mCanExecute;
    private Action<object> mExecute;
    #endregion

    #region Constructors
    public RelayCommand(Predicate<object> canExecute, Action<object> execute)
    {
        mCanExecute = canExecute;
        mExecute = execute;
    }

    public RelayCommand(Action<object> execute)
    {
        mCanExecute = null;
        mExecute = execute;
    }
    #endregion

    #region Events
    public event EventHandler CanExecuteChanged {
        add {
            CommandManager.RequerySuggested += value;
        }
        remove {
            CommandManager.RequerySuggested -= value;
        }
    }
    #endregion

    #region Public Methods
    public bool CanExecute(object parameter)
    {
        if ((mCanExecute == null)) {
            return true;
        }
        return mCanExecute(parameter);
    }

    public void Execute(object parameter)
    {
        mExecute(parameter);
    }
    #endregion

}

и для его использования просто предоставьте свойство типа ICommand, которое возвращает новую функцию RelayCommand с делегатами для функции ...

vb.net

Private mDeleteCommand As ICommand

Public ReadOnly Property DeleteCommand() As ICommand
    Get
        If (mDeleteCommand Is Nothing) Then
            mDeleteCommand = New RelayCommand(AddressOf CanDeleteTodo, AddressOf DeleteTodo)
        End If
        Return mDeleteCommand
    End Get
End Property

C #

private ICommand mDeleteCommand;
public ICommand DeleteCommand {
    get {
        if ((mDeleteCommand == null)) {
            mDeleteCommand = new RelayCommand(CanDeleteTodo, DeleteTodo);
        }
        return mDeleteCommand;
    }
}
1 голос
/ 25 августа 2010

Привет Хулио,

Похоже, это старый пост, но мне очень нравится твой вопрос.

В последнее время я программист на флексах и тоже WPF. Я хорошо знал Cairngorm (позволяет Say [C]) Framework, научился использовать Модель представления с использованием Parsley Framework, и в WPF я понимаю, что Модель представления была изменена на шаблон MVVM.

Команда

Команда в [C] отличается от команды MVVM, команда в [C] более удовлетворена как Шаблон команды , где в [C] контроллер действует как Invoker , поэтому в [C] фактически можно сделать команду для поддержки цепочки, отмены, транзакции и т. д. В MVVM команда далека от шаблона команды. Основная идея использования Command в MVVM, потому что ICommand - единственный способ связывания с операцией. Во Flex вы легко привязываете метод к событию, используя click="{viewmodel.doOperation()}", но не в WPF.

Модель Locator

Это плохая практика - централизовать состояние вашего приложения в одном локаторе моделей, как это сделал [C]. С другой стороны, вы потеряете возможность «легко» модульного тестирования вашего кода, если вы это сделаете. Чем больше зависит ваш код, тем сложнее его протестировать, если локатор модели содержит тонны более мелкой модели, чем вы уже наложили большую зависимость на свой код. И, самое страшное в использовании синглтона - это то, что нельзя издеваться. поэтому, если ваш локатор моделей не подходит для тестирования модулей, тогда процесс тестирования модулей может быть полон боли.

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

С наилучшими пожеланиями

0 голосов
/ 25 августа 2010

Люди, обсуждающие MVVM, сделали его слишком теоретическим, а flex имеет действительно совершенно другую архитектуру, что делает MVVM немного сложным для людей, пришедших из flex.

Позвольте привести очень простой пример,

В Flex в основном мы создаем один MXML для компонента пользовательского интерфейса, и у нас есть наша привязываемая модель, и мы в основном пишем наш код в событиях компонента пользовательского интерфейса, а также компонентов, не являющихся компонентами пользовательского интерфейса. Например, WebService, HttpService и т. Д., Они не являются компонентом пользовательского интерфейса, но они все еще могут находиться внутри MXML, и к ним легко получить доступ в коде MXML.

Таким образом, вы можете легко организовать Model + View + Controller в одном файле MXML.

В Silverlight в XAML вы можете иметь только элементы пользовательского интерфейса в качестве дочерних элементов страницы / пользовательского элемента управления, который вы изменяете. Существуют ограничения: XAML позволяет размещать элемент, не относящийся к пользовательскому интерфейсу, только в ресурсах, а типизированная переменная добавленного вами ресурса не легко доступна в коде позади XAML, для доступа к коду необходимо вызвать ресурс find.

Чтобы упростить задачу, MVVM заставляет вас определять разные файлы.

У вас есть один файл, то есть ваша модель, например Customer.cs

У вас есть другой файл, это ваша ViewModel, которая в основном является комбинацией команд Model +. И вы пишете свой код контроллера в событии «Выполнение команды».

У вас есть другой файл, который является вашим View, представление в основном связывается со всеми свойствами ViewModel, которые являются либо Model, либо Commands.

0 голосов
/ 26 февраля 2010

Хорошо, чтобы дать этой теме какое-то закрытие для дальнейшего использования. Сначала большое спасибо за ответы. RelayCommand действительно хорошая идея; это действительно упрощает и упрощает тестирование и работу. Похоже, это путь. Привязка к SelectedItem также, похоже, решает проблему отсутствия поддержки команд в ItemsControl. Что касается связи между ViewModels, я не уверен, что наличие модели super view решает мою проблему, поскольку она связывает модель с моим визуальным деревом. Кроме того, я не нашел способа в этой структуре иметь чистый, объектно-независимый способ связи между всеми моделями представлений в разных иерархиях. Поэтому я сначала пытаюсь создать централизованную модель, представляющую собой одноэлементную модель, которая реализует интерфейс INotifyPropertyChanged. В этом случае ViewModels может иметь экземпляр этой модели и действовать на нее, распространяя соответствующие изменения свойств, используя наш старый добрый друг шаблон Observer. Кажется, работает хорошо, хотя я немного беспокоюсь о циркулярных ссылках. Что ты думаешь?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...