MVVM Madness: Команды - PullRequest
       32

MVVM Madness: Команды

62 голосов
/ 18 января 2010

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

Позвольте привести пример.

Недавно я хотел подключить команду в моей ViewModel к ListView MouseDoubleClickEvent. Я не был уверен, как это сделать. К счастью, у Google есть ответы на все вопросы. Я нашел следующие статьи:

Хотя решения помогли мне понять команды, были проблемы. Некоторые из вышеперечисленных решений сделали дизайнер WPF непригодным для использования из-за распространенного способа добавления «внутреннего» после свойства зависимости; дизайнер WPF не может найти его, но CLR может. Некоторые из решений не позволили нескольким командам использовать один элемент управления. Некоторые из решений не допускают параметры.

После нескольких часов экспериментов я просто решил сделать это:

private void ListView_MouseDoubleClick(object sender, MouseButtonEventArgs e) {
    ListView lv = sender as ListView;
    MyViewModel vm = this.DataContext as MyViewModel;

    vm.DoSomethingCommand.Execute(lv.SelectedItem);
}

Итак, пуристы MVVM, скажите, пожалуйста, что с этим не так? Я все еще могу проверить мою команду. Это кажется очень практичным, но, кажется, нарушает принцип "ZOMG ... у вас есть код в вашем коде позади !!!!" Пожалуйста, поделитесь своими мыслями.

Заранее спасибо.

Ответы [ 7 ]

37 голосов
/ 18 января 2010

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

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

(Я считаю, что этот аргумент аналогичен аргументам для многопарадигмальных языков. Хотя может применяться подход Pure OO, иногда более подходящим является выполнение некоторых действий более функциональным способом. Хотя может применяться подход Pure Functional, иногда компромисс показать, что методы ОО более чем стоят того.)

13 голосов
/ 18 января 2010

Я согласен с вами, что многие решения MVVM-Command слишком сложны. Лично я использую смешанный подход и определяю свои Команды в View, а не в ViewModel, используя методы и свойства из ViewModel.

XAML:

<Window.Resources>
    <RoutedCommand x:Key="LookupAddressCommand" />
</Window.Resources>
<Window.CommandBindings>
    <CommandBinding Command="{StaticResource LookupAddressCommand}" x:Name="cmdLookupAddress" />
</Window.CommandBindings>

Код (Просмотр):

Private Sub cmdLookupAddress_CanExecute(ByVal sender As System.Object, ByVal e As System.Windows.Input.CanExecuteRoutedEventArgs) Handles cmdLookupAddress.CanExecute
    e.CanExecute = myViewModel.SomeProperty OrElse (myViewModel.SomeOtherProperty = 2)
End Sub

Private Sub cmdLookupAddress_Executed(ByVal sender As System.Object, ByVal e As System.Windows.Input.ExecutedRoutedEventArgs) Handles cmdLookupAddress.Executed
    myViewModel.LookupAddress()
End Sub

Это не чистый MVVM, но он простой, он работает, ему не нужны специальные MVVM-командные классы, и он делает ваш код намного проще для чтения не-MVVM-экспертами (= мои коллеги). *

10 голосов
/ 18 января 2010

Хотя я предпочитаю не писать код-сзади при использовании шаблона MVVM, я думаю, что это нормально, если этот код чисто связан с пользовательским интерфейсом.

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

IЯ думаю, что вы можете легко сделать это в XAML, используя прикрепленное поведение команды .Таким образом, вы можете «привязать» событие MouseDoubleClick к команде вашей модели представления:

<ListView ItemSource="{Binding Items}">
   <local:CommandBehaviorCollection.Behaviors>
      <local:BehaviorBinding Event="MouseDoubleClick" Action="{Binding DoSomething}" />
   </local:CommandBehaviorCollection.Behaviors>

    ...
</ListView>

Вы также можете легко получить доступ к выбранному элементу ListView, не обращаясь к нему напрямую, используяICollectionView интерфейс:

private ICommand _doSomething;

public ICommand DoSomething
{
    get
    {
        if (_doSomething == null)
        {
            _doSomething = new DelegateCommand(
                () =>
                {
                    ICollectionView view = CollectionViewSource.GetDefaultView(Items);
                    object selected = view.CurrentItem;
                    DoSomethingWithItem(selected);
                });
        }
        return _doSomething;
    }
}
5 голосов
/ 18 января 2010

Я считаю, что цель «Нет кода в коде позади» - именно это, цель, которую нужно достичь, а не то, что вы должны воспринимать как абсолютную догму.Есть подходящие места для кода в представлении - и это не обязательно плохой пример того, где или как код может быть проще, чем альтернативный подход.

Преимущество других подходов, которые вы перечисляете, включая присоединенные свойстваили прикрепленные события, это то, что они многоразовые.Когда вы подключаете событие напрямую, а затем делаете то, что делали, очень легко в конечном итоге дублировать этот код во всем приложении.Создавая одно присоединенное свойство или событие для обработки этой разводки, вы добавляете некоторый дополнительный код в сантехнику - но это код, который можно многократно использовать для любого ListView, где вы хотите обработать двойной щелчок.как правило, предпочитают использовать более «пуристский» подход.Исключение всей обработки событий из представления может не повлиять на сценарий тестирования (к которому вы обращаетесь конкретно), но это повлияет на общую проектируемость и ремонтопригодность.Вводя код в свой код, вы ограничиваете свой View тем, что всегда используете ListView с проводным обработчиком событий, что связывает ваш View с кодом и ограничивает гибкость при перепроектировании дизайнером.

2 голосов
/ 07 января 2013

Разделение является одной из основных функций MVVM. Если предположим, что вы хотите изменить, скажем, вид или привязанную модель к нему. Насколько это легко для вашего приложения?

Возьмите пример, когда View1 и View2 оба используют одну и ту же модель представления. Теперь вы реализуете метод кода для обоих.

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

MyViewModel vm = this.DataContext as MyViewModel;

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

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

2 голосов
/ 11 мая 2010

То, что @JP описывает в первоначальном вопросе, а @Heinzi упоминает в том, что он является ответом, - это прагматичный подход к обработке сложных команд. Использование небольшого количества кода для обработки событий в коде сзади особенно удобно, когда вам нужно немного поработать с пользовательским интерфейсом перед вызовом команды.

Рассмотрим классический случай OpenFileDialog. Гораздо проще использовать событие нажатия на кнопку, отобразить диалоговое окно и затем отправить результаты команде в вашей ViewModel, чем принимать какие-либо сложные подпрограммы обмена сообщениями, используемые инструментариями MVVM.

В вашем XAML:

<Button DockPanel.Dock="Left" Click="AttachFilesClicked">Attach files</Button>

В вашем коде позади:

    private void AttachFilesClicked(object sender, System.Windows.RoutedEventArgs e)
    {
        // Configure open file dialog box
        Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog();
        dlg.FileName = "Document"; // Default file name
        dlg.DefaultExt = ".txt"; // Default file extension
        dlg.Filter = "Text documents (.txt)|*.txt"; // Filter files by extension

        // Show open file dialog box
        bool? result = dlg.ShowDialog();

        // Process open file dialog box results
        if (result == true)
        {
            string filename = dlg.FileName;

            // Invoke the command.
            MyViewModel myViewModel = (MyViewModel)DataContext;
            if (myViewModel .AttachFilesCommand.CanExecute(filename))
            {
                noteViewModel.AttachFilesCommand.Execute(filename);  
            }
        }
    }

Компьютерное программирование негибко. Мы, программисты, должны быть гибкими, чтобы иметь дело с этим.

1 голос
/ 18 марта 2010

Командование для чурбанов. Настоящие мужчины подключают весь свой интерфейс к событиям в коде позади.

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