MVVM безумие: команды



Мне нравится 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... у вас есть код в ваш код-позади!!!!"Пожалуйста, поделитесь своими мыслями.



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

728   7  

7 ответов:

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

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

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

Я согласен с вами, что многие MVVM-командные решения слишком сложны. Лично я использую смешанный подход и определяю свои команды в представлении, а не в 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-command-классы, и это делает ваш код намного проще читать для не-MVVM-экспертов (= my сотрудники.)

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

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

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

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

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

преимущество других подходов, которые вы перечисляете, включая вложенные свойства или вложенные события, заключается в том, что они повторно используются. Когда вы подключаете событие непосредственно, то что вы сделали, это очень легко в конечном итоге дублирования кода в приложении. Создавая одно прикрепленное свойство или событие для обработки этой проводки, вы добавляете дополнительный код в водопровод - но это код, который можно повторно использовать для любого ListView, где вы хотите дважды щелкнуть обработку.

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

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

рассмотрим классический случай OpenFileDialog. Гораздо проще использовать событие click на кнопке, отобразить диалоговое окно, а затем отправить результаты в команду на вашем 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);  
            }
        }
    }

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

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

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

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

MyViewModel vm = this.DataContext as MyViewModel;

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

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

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

Comments

    Ничего не найдено.