Безумие 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... у Вас есть код в Вашем коде - позади!!!!" Совместно используйте свои мысли.

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

62
задан JP Richardson 18 January 2010 в 17:41
поделиться

6 ответов

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

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

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

.
37
ответ дан 24 November 2019 в 16:53
поделиться

Вы можете использовать более мощный профилировщик, такой как Intel ® VTune, который может предоставить вам детальную информацию о производительности сборочной линии.

http://software.intel.com/en-us/intel-vtune/

Это для Windows и Linux, но стоит денег...

-121--1687205-
def myMethod(callback : Int => Unit, size : => Int) = ...

myMethod(a.add _, a.size)

Пустота представлена посредством типа Unit .

Edit: При использовании = > Int , size передается по имени, что означает, что он всегда пересчитывается при необходимости.

-121--4690940-

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

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

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

5
ответ дан 24 November 2019 в 16:53
поделиться

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

XAML:

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

код (View):

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-эксперты (= мои сотрудники).

13
ответ дан 24 November 2019 в 16:53
поделиться

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

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

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

<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;
    }
}
10
ответ дан 24 November 2019 в 16:53
поделиться

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

1
ответ дан 24 November 2019 в 16:53
поделиться

То, что @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);  
            }
        }
    }

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

2
ответ дан 24 November 2019 в 16:53
поделиться
Другие вопросы по тегам:

Похожие вопросы: