В этой главе мы рассмотрим важную технику разработки программного обеспечения Silverlight — использование View Models .
-
Модель представления является ключевой частью, которая вводит технику, называемую разделенным представлением, сохраняя представление отдельно от модели.
-
Модели представлений предлагают один из способов достижения отдельного представления, и мы увидим, как они используют привязку данных Silverlight для уменьшения объема кода, необходимого в вашем пользовательском интерфейсе.
Модель представления является ключевой частью, которая вводит технику, называемую разделенным представлением, сохраняя представление отдельно от модели.
Модели представлений предлагают один из способов достижения отдельного представления, и мы увидим, как они используют привязку данных Silverlight для уменьшения объема кода, необходимого в вашем пользовательском интерфейсе.
Проблемы разработки пользовательского интерфейса
Модели View предназначены для решения определенных проблем, которые часто возникают при разработке программного обеспечения пользовательского интерфейса. Возможно, наиболее важным из них является то, что код пользовательского интерфейса часто сложно неразрывно протестировать, особенно с помощью автоматических модульных тестов. Есть также проблемы с качеством кода, которые могут повлиять на постоянную гибкость и ремонтопригодность вашего кода.
-
Если вы пойдете по пути наименьшего сопротивления, к которому вас привели инструменты проектирования Visual Studio, вы можете в итоге поместить слишком много кода в код позади.
-
Очень часто можно увидеть большое количество функциональности приложения, добавленной в код.
-
Мало кто из разработчиков на самом деле планировал бы поместить бизнес-логику в класс пользовательского интерфейса, но поскольку именно туда Visual Studio помещает ваши обработчики событий, это становится слишком удобным местом для выполнения задач.
-
Широко признано, что программное обеспечение легче разрабатывать и поддерживать, если классы имеют четко определенные и достаточно узкие обязанности.
-
Задача кода заключается в непосредственном взаимодействии с объектами, составляющими пользовательский интерфейс, где это необходимо.
-
Как только вы начинаете размещать код, который принимает решения о том, как ваше приложение ведет себя там, что может привести к проблемам.
-
Мало того, что логика приложения может перетекать в код, который должен быть связан с пользовательским интерфейсом, некоторые разработчики начинают полагаться на элементы управления и другие объекты пользовательского интерфейса для поддержания важного состояния приложения.
-
Модель просто содержит данные, представление просто содержит отформатированную дату, а контроллер (ViewModel) выступает в качестве связующего звена между ними. Контроллер может принимать входные данные из вида и размещать их на модели и наоборот.
Если вы пойдете по пути наименьшего сопротивления, к которому вас привели инструменты проектирования Visual Studio, вы можете в итоге поместить слишком много кода в код позади.
Очень часто можно увидеть большое количество функциональности приложения, добавленной в код.
Мало кто из разработчиков на самом деле планировал бы поместить бизнес-логику в класс пользовательского интерфейса, но поскольку именно туда Visual Studio помещает ваши обработчики событий, это становится слишком удобным местом для выполнения задач.
Широко признано, что программное обеспечение легче разрабатывать и поддерживать, если классы имеют четко определенные и достаточно узкие обязанности.
Задача кода заключается в непосредственном взаимодействии с объектами, составляющими пользовательский интерфейс, где это необходимо.
Как только вы начинаете размещать код, который принимает решения о том, как ваше приложение ведет себя там, что может привести к проблемам.
Мало того, что логика приложения может перетекать в код, который должен быть связан с пользовательским интерфейсом, некоторые разработчики начинают полагаться на элементы управления и другие объекты пользовательского интерфейса для поддержания важного состояния приложения.
Модель просто содержит данные, представление просто содержит отформатированную дату, а контроллер (ViewModel) выступает в качестве связующего звена между ними. Контроллер может принимать входные данные из вида и размещать их на модели и наоборот.
Отдельная презентация
Чтобы избежать проблем, вызванных размещением логики приложения в коде или XAML, лучше всего использовать технику, известную как разделенное представление . Имея XAML и код с минимальными затратами, необходимыми для непосредственной работы с объектами пользовательского интерфейса, классы пользовательского интерфейса также содержат код для сложных поведений взаимодействия, логику приложения и все остальное, как показано ниже на левой стороне.
Важные особенности отдельной презентации —
-
С раздельным представлением класс пользовательского интерфейса намного проще. Конечно, в нем есть XAML, но код, стоящий за ним, делает настолько мало, насколько это практически возможно.
-
Логика приложения принадлежит к отдельному классу, который часто называют моделью .
-
Многие разработчики пытаются использовать привязку данных для непосредственного подключения элементов в XAML к свойствам в модели.
-
Проблема в том, что модель целиком связана с тем, что делает приложение, а не с тем, как пользователь взаимодействует с приложением.
-
Большинство пользовательских интерфейсов имеют некоторое состояние, которое не относится к модели приложения. Например, если ваш пользовательский интерфейс использует перетаскивание, необходимо что-то отслеживать, например, где находится перетаскиваемый элемент, как должен изменяться его внешний вид при перемещении по возможным объектам перетаскивания, и как эти объекты могут также изменить, как элемент перетаскивается на них.
-
Такое состояние может быть на удивление сложным и нуждается в тщательном тестировании.
-
На практике вы обычно хотите, чтобы какой-то другой класс находился между пользовательским интерфейсом и моделью. Это имеет две важные роли.
-
Во-первых, он адаптирует модель вашего приложения для конкретного представления пользовательского интерфейса.
-
Во-вторых, именно там живет любая нетривиальная логика взаимодействия, и под этим я подразумеваю код, необходимый для того, чтобы ваш пользовательский интерфейс работал так, как вы хотите.
-
С раздельным представлением класс пользовательского интерфейса намного проще. Конечно, в нем есть XAML, но код, стоящий за ним, делает настолько мало, насколько это практически возможно.
Логика приложения принадлежит к отдельному классу, который часто называют моделью .
Многие разработчики пытаются использовать привязку данных для непосредственного подключения элементов в XAML к свойствам в модели.
Проблема в том, что модель целиком связана с тем, что делает приложение, а не с тем, как пользователь взаимодействует с приложением.
Большинство пользовательских интерфейсов имеют некоторое состояние, которое не относится к модели приложения. Например, если ваш пользовательский интерфейс использует перетаскивание, необходимо что-то отслеживать, например, где находится перетаскиваемый элемент, как должен изменяться его внешний вид при перемещении по возможным объектам перетаскивания, и как эти объекты могут также изменить, как элемент перетаскивается на них.
Такое состояние может быть на удивление сложным и нуждается в тщательном тестировании.
На практике вы обычно хотите, чтобы какой-то другой класс находился между пользовательским интерфейсом и моделью. Это имеет две важные роли.
Во-первых, он адаптирует модель вашего приложения для конкретного представления пользовательского интерфейса.
Во-вторых, именно там живет любая нетривиальная логика взаимодействия, и под этим я подразумеваю код, необходимый для того, чтобы ваш пользовательский интерфейс работал так, как вы хотите.
Модель / Вид / ВидМодель
Модель представления — это пример подхода с разделенной презентацией, но давайте будем четко понимать, что именно мы имеем на каждом уровне. Есть три слоя —
- модель
- Посмотреть
- ViewModel
модель
Это классическая объектная модель, состоящая из обычных классов C #, которая не имеет прямой связи с пользовательским интерфейсом.
Обычно вы ожидаете, что ваши коды моделей смогут компилироваться без ссылок на какие-либо библиотеки пользовательского интерфейса. Фактически, вы, вероятно, сможете взять точно такой же исходный код и скомпилировать его в приложение Silverlight, обычное консольное приложение .NET или даже веб-код на стороне сервера.
Типы в Модели должны представлять концепции, с которыми работает ваше приложение.
Посмотреть
Представление — это обычно UserControl, это может быть ваша главная страница или просто часть вашей страницы.
В большинстве приложений Silverlight рекомендуется разделить пользовательский интерфейс на маленькие части, определяющие UserControl или View для каждой части.
Приложения Silverlight не уникальны в этом отношении. Кое-что, очевидно, является специфическим для Silverlight, это View. Чем более детализирован ваш пользовательский интерфейс, тем лучше. Мало того, что у вас меньше шансов запутаться над другими разработчиками, работающими над одними и теми же файлами, что делает их небольшими и простыми, естественно, препятствует использованию ярлыков, которые приводят к спагетти-подобному коду.
Например, очень часто определяют представление для представления отдельного элемента в списке.
ViewModel
Наконец, для каждого View вы пишете ViewModel . Итак, это одна из важных функций класса ViewModel .
Он существует, чтобы служить определенному мнению. ViewModel специализируется на конкретном способе представления вещей, таких как конкретный элемент данных, как он отображается в списках.
Вот почему он называется ViewModel ; он адаптирует базовую модель специально для конкретного представления. Как и модель, ViewModel также является обычным классом C #. Это не должно происходить от какого-либо конкретного типа.
Как это бывает, некоторые разработчики считают удобным помещать некоторые общие функциональные возможности в базовый класс ViewModel, но шаблон этого не требует. В частности, ваша ViewModel не является производной от какого-либо определенного типа Silverlight. Однако, в отличие от модели, он может использовать типы Silverlight в своих свойствах.
Например, ваша ViewModel может решить сделать определенные части вашего пользовательского интерфейса видимыми только при определенных условиях, поэтому вы можете предоставить свойство типа System.Windows.Visibility, которое является типом элементов Silverlight, используемых для их свойства Visibility. Это позволяет связать видимость элемента, такого как панель, непосредственно с ViewModel.
пример
Давайте рассмотрим простой пример, в котором мы будем использовать подход Model-View-ViewModel (MVVM) .
Шаг 1 — Создайте новый проект приложения Silverlight SilverlightMVVMDemo .
Шаг 2 — Добавьте три папки (Model, ViewModel и Views) в ваш проект, как показано ниже.
Шаг 3 — Добавьте класс StudentModel в папку Model и вставьте приведенный ниже код в этот класс.
using System.ComponentModel; namespace SilverlightMVVMDemo.Model { public class StudentModel {} public class Student : INotifyPropertyChanged { private string firstName; private string lastName; public string FirstName { get { return firstName; } set { if (firstName != value) { firstName = value; RaisePropertyChanged("FirstName"); RaisePropertyChanged("FullName"); } } } public string LastName { get { return lastName; } set { if (lastName != value) { lastName = value; RaisePropertyChanged("LastName"); RaisePropertyChanged("FullName"); } } } public string FullName { get { return firstName + " " + lastName; } } public event PropertyChangedEventHandler PropertyChanged; private void RaisePropertyChanged(string property) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(property)); } } } }
Шаг 4 — Добавьте другой класс StudentViewModel в папку ViewModel и вставьте следующий код.
using SilverlightMVVMDemo.Model; using System.Collections.ObjectModel; namespace SilverlightMVVMDemo.ViewModel { public class StudentViewModel { public ObservableCollection<Student> Students { get; set; } public void LoadStudents() { ObservableCollection<Student> students = new ObservableCollection<Student>(); students.Add(new Student { FirstName = "Mark", LastName = "Allain" }); students.Add(new Student { FirstName = "Allen", LastName = "Brown" }); students.Add(new Student { FirstName = "Linda", LastName = "Hamerski" }); Students = students; } } }
Шаг 5. Добавьте пользовательский элемент управления Silverlight , щелкнув правой кнопкой мыши папку « Виды » и выберите « Добавить новый элемент…» .
Шаг 6 — Нажмите Добавить. Теперь вы увидите файл XAML. Добавьте следующий код в файл StudentView.xaml , который содержит различные элементы пользовательского интерфейса.
<UserControl x:Class = "SilverlightMVVMDemo.Views.StudentView" xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d = "http://schemas.microsoft.com/expression/blend/2008" xmlns:mc = "http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable = "d" d:DesignHeight = "300" d:DesignWidth = "400"> <Grid x:Name = "LayoutRoot" Background = "White"> <StackPanel HorizontalAlignment = "Left"> <ItemsControl ItemsSource = "{Binding Path=Students}"> <ItemsControl.ItemTemplate> <DataTemplate> <StackPanel Orientation = "Horizontal"> <TextBox Text = "{Binding Path = FirstName, Mode = TwoWay}" Width = "100" Margin = "3 5 3 5"/> <TextBox Text = "{Binding Path = LastName, Mode = TwoWay}" Width = "100" Margin = "0 5 3 5"/> <TextBlock Text = "{Binding Path = FullName, Mode=OneWay}" Margin = "0 5 3 5"/> </StackPanel> </DataTemplate> </ItemsControl.ItemTemplate> </ItemsControl> </StackPanel> </Grid> </UserControl>
Шаг 7 — Теперь добавьте StudentView в файл MainPage.xaml, как показано ниже.
<UserControl x:Class = "SilverlightMVVMDemo.MainPage" xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d = "http://schemas.microsoft.com/expression/blend/2008" xmlns:mc = "http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:views = "clr-namespace:SilverlightMVVMDemo.Views" mc:Ignorable = "d" d:DesignHeight = "576.316" d:DesignWidth = "863.158"> <Grid x:Name = "LayoutRoot" Background = "White"> <views:StudentView x:Name = "StudentViewControl" Loaded = "StudentViewControl_Loaded"/> </Grid> </UserControl>
Шаг 8 — Вот реализация события Loaded в файле MainPage.xaml.cs , который обновит View из ViewModel .
using System.Windows; using System.Windows.Controls; namespace SilverlightMVVMDemo { public partial class MainPage : UserControl { public MainPage() { InitializeComponent(); } } private void StudentViewControl_Loaded(object sender, RoutedEventArgs e) { SilverlightMVVMDemo.ViewModel.StudentViewModel studentViewModelObject = new SilverlightMVVMDemo.ViewModel. StudentViewModel(); studentViewModelObject.LoadStudents(); StudentViewControl.DataContext = studentViewModelObject; } }
Шаг 9 — Когда приведенный выше код скомпилирован и выполнен, вы увидите следующий вывод на своей веб-странице.
Пользовательский интерфейс против ViewModel
Одна из самых сложных частей подхода MVVM — определить, где должна проходить разделительная линия. Не всегда очевидно, какие вещи принадлежат где.
-
В частности, некоторые элементы пользовательского интерфейса обеспечивают функциональность, которая, согласно строгому представлению, вероятно, должна принадлежать ViewModel.
-
В общем, не все варианты поведения, реализованные в View , настолько удобны для ViewModel .
-
Одной из причин этого является то, что не существует какого-либо стандартного способа упаковки поведения ViewModel для повторного использования, особенно если вы не хотите использовать среду разработки, такую как Visual Studio или Blend.
В частности, некоторые элементы пользовательского интерфейса обеспечивают функциональность, которая, согласно строгому представлению, вероятно, должна принадлежать ViewModel.
В общем, не все варианты поведения, реализованные в View , настолько удобны для ViewModel .
Одной из причин этого является то, что не существует какого-либо стандартного способа упаковки поведения ViewModel для повторного использования, особенно если вы не хотите использовать среду разработки, такую как Visual Studio или Blend.
Преимущества МВВМ
MVVM предлагает следующие преимущества —
-
Разделение проблем презентации (View, ViewModel, Model)
-
Чистый тестируемый и управляемый код. Может включать логику уровня представления в модульном тестировании.
-
За кодом нет кода, поэтому уровень представления и логика слабо связаны.
-
Лучший способ привязки данных.
Разделение проблем презентации (View, ViewModel, Model)
Чистый тестируемый и управляемый код. Может включать логику уровня представления в модульном тестировании.
За кодом нет кода, поэтому уровень представления и логика слабо связаны.
Лучший способ привязки данных.
Недостатки MVVM
Для простых пользовательских интерфейсов MVVM может быть излишним. Отладка будет немного сложнее, когда у нас сложные привязки данных.