Хорошие примеры Шаблона MVVM

Мы должны были определить, был ли C# сопоставим с C++ в производительности, и я записал некоторые тестовые программы для того (использующий Visual Studio 2005 для обоих языков). Оказалось, что без сборки "мусора" и только рассмотрения языка (не платформа) C# имеет в основном то же представление в качестве C++. Выделение памяти является путем быстрее в C#, чем в C++, и C# имеет небольшой край в детерминизме, когда размеры данных увеличены вне границ строки кэша. Однако все это нужно было в конечном счете оплатить и существует огромная стоимость в форме недетерминированных хитов производительности для C# из-за сборки "мусора".

139
задан Community 23 May 2017 в 10:31
поделиться

7 ответов

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

Выпуск призмы:
http://www.codeplex.com/CompositeWPF

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

Prism не обязательно для каждого проекта, но с ней полезно ознакомиться.

CRUD: Эта часть довольно проста, двусторонние привязки WPF позволяют очень легко редактировать большинство данных. Настоящая уловка состоит в том, чтобы предоставить модель, которая упрощает настройку пользовательского интерфейса. По крайней мере, вы хотите убедиться, что ваша ViewModel (или бизнес-объект) реализует INotifyPropertyChanged для поддержки привязки, и вы можете привязать свойства прямо к элементам управления пользовательского интерфейса, но вы также можете реализовать IDataErrorInfo для проверки. Обычно, если вы используете какое-то решение ORM, настроить CRUD несложно.

В этой статье демонстрируются простые грубые операции: http://dotnetslackers.com/articles/wpf/WPFDataBindingWithLINQ.aspx

Он построен на LinqToSql, но это не имеет отношения к примеру - все, что важно, это то, что ваши бизнес-объекты реализуют INotifyPropertyChanged (какие классы, сгенерированные LinqToSql, делают). MVVM не является предметом этого примера, но я не думаю, что это имеет значение в данном случае.

Эта статья демонстрирует проверку данных
http://blogs.msdn.com/wpfsdk/archive/2007/10/02/data-validation-in-3-5.aspx

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

В большинстве случаев вы можете взять объект (модель), созданный некоторым ORM, и обернуть его в ViewModel, который содержит его, и команды для сохранить / удалить - и вы готовы привязать пользовательский интерфейс прямо к свойствам модели.

Представление будет выглядеть примерно так (ViewModel имеет свойство Item , которое содержит модель, как класс, созданный в ORM):

<StackPanel>
   <StackPanel DataContext=Item>
      <TextBox Text="{Binding FirstName, Mode=TwoWay, ValidatesOnDataErrors=True}" />
      <TextBox Text="{Binding LastName, Mode=TwoWay, ValidatesOnDataErrors=True}" />
   </StackPanel>
   <Button Command="{Binding SaveCommand}" />
   <Button Command="{Binding CancelCommand}" />
</StackPanel>

Диалоги: Диалоги и MVVM немного сложны. Я предпочитаю использовать разновидность подхода Mediator с диалогами, вы можете прочитать об этом немного больше в этом вопросе StackOverflow:
Пример диалога WPF MVVM

Мой обычный подход, который не совсем классический MVVM, можно резюмировать следующим образом:

Базовый класс для диалоговой модели ViewModel, который предоставляет команды для действий фиксации и отмены, событие, позволяющее представление знает, что диалог готов к закрытию, и все, что вам понадобится во всех ваших диалогах.

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

Где-то вам нужно предоставить шаблоны данных для ваших ViewModels, они могут быть очень простыми, тем более что у вас, вероятно, есть представление для каждого диалогового окна, инкапсулированного в отдельный элемент управления. Тогда шаблон данных по умолчанию для ViewModel будет выглядеть примерно так:

<DataTemplate DataType="{x:Type vmodels:AddressEditViewModel}">
   <views:AddressEditView DataContext="{Binding}" />
</DataTemplate>

Диалоговое представление должно иметь доступ к ним, потому что в противном случае оно не будет знать, как показать ViewModel, кроме общего диалогового UI, его содержимое в основном это:

<ContentControl Content="{Binding}" />

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

Это не совсем mvvm часть. Один из способов сделать это - использовать глобальное событие. Я думаю, что лучше всего использовать настройку типа агрегатора событий, предоставляемую посредством внедрения зависимостей - таким образом, событие является глобальным для контейнера, не все приложение. Prism использует структуру единства для семантики контейнера и внедрения зависимостей, и в целом мне очень нравится Unity.

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

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

Однако бывают случаи, когда пользовательский интерфейс должен поднимать диалоги, что может немного усложнить задачу. Рассмотрим, например, зависит ли положение диалогового окна от положения кнопки, которая его открывает. В этом случае вам понадобится некоторая информация, специфичная для пользовательского интерфейса, когда вы запрашиваете диалог. Обычно я создаю отдельный класс, который содержит ViewModel и некоторую соответствующую информацию пользовательского интерфейса. К сожалению, некоторая связь здесь кажется неизбежной.

Псевдокод обработчика кнопки, который вызывает диалоговое окно, в котором требуются данные о положении элемента:

ButtonClickHandler(sender, args){
    var vm = DataContext as ISomeDialogProvider; // check for null
    var ui_vm = new ViewModelContainer();
    // assign margin, width, or anything else that your custom dialog might require
    ...
    ui_vm.ViewModel = vm.SomeDialogViewModel; // or .GetSomeDialogViewModel()
    // raise the dialog show event
}

Представление диалогового окна привязывается к данным положения и передает содержащуюся в нем ViewModel во внутренний ContentControl . Сама ViewModel по-прежнему ничего не знает о пользовательском интерфейсе.

Как правило, я не использую свойство возврата DialogResult метода ShowDialog () и не ожидаю, что поток будет блокироваться до закрытия диалогового окна. Нестандартный модальный диалог не всегда работает подобным образом, а в составной среде вы часто не хотите, чтобы обработчик событий блокировал подобное. Я предпочитаю позволить ViewModel заниматься этим - создатель ViewModel может подписаться на соответствующие события, установить методы фиксации / отмены и т.д., поэтому нет необходимости полагаться на этот механизм пользовательского интерфейса.

Итак, вместо этого потока:

// in code behind
var result = somedialog.ShowDialog();
if (result == ...

Я использую:

// in view model
var vm = new SomeDialogViewModel(); // child view model
vm.CommitAction = delegate { this.DoSomething(vm); } // what happens on commit 
vm.CancelAction = delegate { this.DoNothing(vm); } // what happens on cancel/close (optional)
// raise dialog request event on the container

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

58
ответ дан 23 November 2019 в 23:22
поделиться

Вы смотрели Калибурн ? В образце ContactManager много хороших вещей. Общие образцы WPF также предоставляют хороший обзор команд. Документация довольно хорошая, форумы активны. Рекомендуется!

3
ответ дан 23 November 2019 в 23:22
поделиться

Образец проекта в Cinch framework показывает основные CRUD и инструменты навигации. Это довольно хороший пример использования MVVM и включает в себя статью, состоящую из нескольких частей , объясняющую его использование и мотивацию.

2
ответ дан 23 November 2019 в 23:22
поделиться

Я также разделял ваше разочарование. Я пишу приложение, и у меня были следующие 3 требования:

  • Расширяемый
  • WPF с MVVM
  • Примеры, совместимые с GPL

Все, что я нашел, были кусочками и кусочками, поэтому я просто начал писать его как можно лучше . После того, как я немного погрузился в это, я понял, что могут быть другие люди (такие как вы), которые могут использовать эталонное приложение, поэтому я реорганизовал общий материал в структуру приложения WPF / MVVM и выпустил его под LGPL. Я назвал его SoapBox Core . Если вы перейдете на страницу загрузок, вы увидите, что оно поставляется с небольшим демонстрационным приложением, и исходный код этого демонстрационного приложения также доступен для загрузки. Надеюсь, вы найдете это полезным. Кроме того, напишите мне на scott {at} soapboxautomation.com, если вам нужна дополнительная информация.

EDIT : Также опубликована статья CodeProject , объясняющая, как это работает.

2
ответ дан 23 November 2019 в 23:22
поделиться

Jason Dolinger made a good screencast of MVVM. Like Egor mentioned there is no one good example. They are all over. Most are good MVVM examples, but not when you get into complex issues. Everyone has their own way. Laurent Bugnion has a good way to communicate between viewmodels as well. http://blog.galasoft.ch/archive/2009/09/27/mvvm-light-toolkit-messenger-v2-beta.aspx Cinch is also a good example. Paul Stovel has a good post that explains a lot too with his Magellan framework.

6
ответ дан 23 November 2019 в 23:22
поделиться

Учитывали ли вы nested_attributes для моделей рельсов? Вместо нескольких новых форм обратной связи, каждая из которых связана с аннотацией, можно использовать несколько редактируемых форм аннотации, в которых каждая аннотация включает поля для новой обратной связи. Идентификаторы созданных форм будут включать идентификатор аннотаций, например edit _ annotation _ 16 .

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

class Annotation < ActiveRecord::Base
  has_many :feedbacks
  accepts_nested_attributes_for :feedbacks
end

class Feedback < ActiveRecord::Base
  belongs_to :annotation
end

Затем можно добавить любое количество форм, по одной для каждой аннотации. Например, это то, что я пытался:

<% form_for @a do |form| %>
    Lyrics: <br />
    <%= form.text_field :lyrics %><br />
    <% form.fields_for :feedbacks do |feedback| %>
        Feedback: <br/>
        <%= feedback.text_field :response %><br />
    <% end %>
    <%= form.submit "Submit" %>
<% end %>

<% form_for @b do |form| %>
    Lyrics: <br />
    <%= form.text_field :lyrics %><br />
    <% form.fields_for :feedbacks do |feedback| %>
        Feedback: <br/>
        <%= feedback.text_field :response %><br />
    <% end %>
    <%= form.submit "Submit" %>
<% end %>

И быстрый и грязный контроллер для вышеприведенного вида редактирования:

class AnnotationsController < ApplicationController
  def edit
    @a = Annotation.find(1)
    @a.feedbacks.build
    @b = Annotation.find(2)
    @b.feedbacks.build
  end

  def update
    @annotation = Annotation.find(params[:id])
    @annotation.update_attributes(params[:annotation])
    @annotation.save!
    render :index
  end
end
-121--3802360-

записать их прыжки от единичных тестов к коду к единичному тесту к коду... и так далее.

-121--3505004-

Даже я разделял разочарование, пока не взял дело в свои руки. Я начал IncEditor.

IncEditor ( http://inceditor.codeplex.com ) - редактор, который пытается познакомить разработчиков с WPF, MVVM & MEF. Я начал его и сумел получить некоторые функциональные возможности, как 'тема' поддержки. Я не являюсь экспертом в WPF, MVVM или MEF, поэтому я не могу вложить в это много функциональных возможностей. Я искренне прошу вас, ребята, сделать это лучше, чтобы такие, как я, могли лучше понять это.

1
ответ дан 23 November 2019 в 23:22
поделиться

Этот вариант полезен . Также есть код.

http://msdn.microsoft.com/en-us/magazine/dd419663.aspx

3
ответ дан 23 November 2019 в 23:22
поделиться
Другие вопросы по тегам:

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