Ответ 1
Нет. Вы не подвергаете StockQuote. Вы только указываете (слабо типизированный) интерфейс в представлении. Представление знает только два свойства: Символ и цена. Вы можете легко заменить StockQuote чем-нибудь еще, пока он реализует их.
Точка M-V-VM, как мы все знаем, связана с разгадкой проблем. В таких шаблонах, как MVVM, MVC или MVP, основная цель - отделить представление от данных, создав тем самым более гибкие компоненты. Сначала я продемонстрирую очень распространенный сценарий, который можно найти во многих приложениях WPF, а затем сделаю следующее:
Скажем, у нас есть приложение StockQuote, которое передает поток котировок и отображает их на экране. Как правило, у вас будет следующее:
StockQuote.cs: (Модель)
public class StockQuote
{
public string Symbol { get; set; }
public double Price { get; set; }
}
StockQuoteViewModel.cs: (ViewModel)
public class StockQuoteViewModel
{
private ObservableCollection<StockQuote> _quotes = new ObservableCollection<StockQuote>();
public ObservableCollection<StockQuote> Quotes
{
get
{
return _quotes;
}
}
}
StockQuoteView.xaml(Просмотр)
<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
Title="Window1" Height="300" Width="300">
<Window.DataContext>
<local:StockQuoteViewModel/>
</Window.DataContext>
<Window.Resources>
<DataTemplate x:Key="listBoxDateTemplate">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Symbol}"/>
<TextBlock Text="{Binding Price}"/>
</StackPanel>
</DataTemplate>
</Window.Resources>
<Grid>
<ListBox ItemTemplate="{StaticResource listBoxDateTemplate}" ItemsSource="{Binding Quotes}"/>
</Grid>
</Window>
И тогда у вас будет какая-то услуга, которая будет кормить ObservableCollection новыми StockQuotes.
Мой вопрос таков: в этом типе сценария StockQuote считается моделью, и мы раскрываем это для просмотра через ViewModel ObservableCollection. Что в основном означает, наш взгляд обладает знаниями о Модели. Разве это не нарушает всю парадигму M-V-VM? Или я тут что-то пропустил...?
Нет. Вы не подвергаете StockQuote. Вы только указываете (слабо типизированный) интерфейс в представлении. Представление знает только два свойства: Символ и цена. Вы можете легко заменить StockQuote чем-нибудь еще, пока он реализует их.
Я больше знаком с MVC, чем MVVM, но общепризнано, что представление будет иметь представление о Модели. Пока модель не знает представления, это нормально.
Если это действительно вызывает беспокойство по какой-либо причине, посмотрите дизайн "Пассивный вид", в котором "Вид" не знает ничего, кроме необработанных данных, поданных на него.
В MVVM модель представления представляет собой нечто среднее между представлением и моделью, которая предоставляет данные из модели таким образом, чтобы ее можно было легко просматривать. В строгом приложении MVVM представление не знает о модели, только о модели представления.
В вашем конкретном примере модель представления не должна называться StockQuoteViewModel
, но StockQuotesViewModel
(помните о множественном числе), потому что модель представления подвергает много котировок акций определенной коллекцией ui, которую легко обрабатывать по представлению (потому что ObservableCollection<T>
реализует INotifyCollectionChanged<T>
). Тип элементов в коллекции должен быть моделью просмотра (например, StockQuoteViewModel
), которая предоставляет данные из одного объекта StockQuote. В такой модели просмотра вы можете добавить логику, например, добавить $
-symbol to Price
и так далее.
Часто бывает проще выставить некоторые модельные объекты в модели представления, но правильным способом было бы создать модель представления для каждого класса модели.
С наилучшими пожеланиями,
Оливер Ханаппи
Посмотрите видео: Джейсон Долинджер на MVVM. Он ответит на ваш вопрос.
Кроме того, см. вопрос SO wpf mvvm confusion для дополнительных ресурсов.
Я понимаю, что модели ViewModels относятся к моделям как свойства для полей. Это очень простая аналогия, но это подразумевает, что вы не изолированы должным образом, если ваш просмотр напрямую обращается к вашей модели. Как и в случае с тривиальными свойствами в классе, обертывающим частные поля, вы получаете много дублирования и код шаблона при упаковке соответствующих свойств модели в свойствах ViewModel для использования View. Это то, что беспокоит меня с этим шаблоном, и я до сих пор не решил, выгодны ли выгоды для раздувания.
В этом конкретном примере я думаю, что было бы слишком сложно создать виртуальную машину для каждого экземпляра StockQuote, так как вы вряд ли будете делать какую-либо значительную логику для представления, представляющего отдельную StockQuote. Я думаю, что в этих маленьких случаях он намного чище и удобнее обслуживать, чтобы просто привязываться к классу модели напрямую. Создание виртуальной машины для малого случая уменьшило бы связь, но это также увеличило бы сложность, и я думаю, что это решение в каждом конкретном случае касается того, выгодно ли это.
Возможно, у меня есть это неправильно, но не идея модели представления полностью инкапсулировать модель. Например, у вас есть котировки акций, выставленные для представления, но они должны быть сопоставлены с свойствами, родными для viewmodel, которые затем будут привязаны к. Это требует "очистки", которая может потребоваться при передаче данных в модель/представление.
Таким образом, представление только когда-либо знает модель просмотра. Это также означает, что, если модель не была устаревшей, она может быть реализована как интерфейс и дополнительно уменьшить связь между моделью просмотра.