Цель работы: освоить основные понятия MVVM
Простейшее приложение для Windows Phone, всеми аспектами которого занимается один разработчик, вполне можно написать, не задумываясь о том, чтобы разделять код, имеющий отношение к визуализации данных, к их обработке и получению из некоторых источников. Приложение, в котором большая часть кода, ответственного за всю его функциональность, собрана в программном файле к его начальной странице (особенно, когда эта функциональность требует пары десятков строк кода), будет работать. Но небольшой рост сложности приложения приводит к тому, что таким кодом становится очень неудобно управлять. Его неудобно расширять, тестировать, неудобно привлекать других программистов к работе над приложением. Небольшое изменение в коде может повлечь за собой множество неожиданных, неприятных последствий. Идея разделения кода, который ответственен за работоспособность различных подсистем приложения, это одна из идей, которая легла в основу создания шаблонов проектирования приложений. Один из таких шаблонов, получивший популярность в последние годы, называется MVVM, Model-View-ViewModel, Модель-Представление-Модель представления. Этот шаблон используют на разных платформах, он универсален и позволяет создавать крупные программные проекты, обладающие чёткой и понятной структурой. Для того чтобы приступить к использованию MVVM, нужно понять сущность некоторых основных понятий, которые имеют к отношение к этому шаблону проектирования.
Очень кратко сущность MVVM можно выразить так: "Модель содержит исходные данные, которые готовятся к выводу с помощью модели представления и отображаются пользователю с помощью представления".
Рассмотрим это более подробно.
Представление (View). Это та часть приложения, которая формирует пользовательский интерфейс и отвечает за взаимодействие с пользователем. К представлению относится XAML-разметка страницы, отображаемые на странице элементы управления, анимации, которые применяются к элементам управления и логика, которая имеет отношение к интерфейсу. В идеальном варианте представление – это исключительно XAML-код, который описывает страницу и элементы управления, а в файле программного кода к этой странице содержится лишь конструктор, который вызывается при создании страницы. Для отображения интерфейса, однако, часто требуются какие-либо специфические операции, которые так же реализуют в файлах кода страниц. Представление, конечно, не используется автономно, никому не нужен интерфейс, который не отображает полезных данных и не способен обеспечить взаимодействие пользователя с этими данными. В том, что касается визуализации каких-либо списков данных, а подобные интерфейсы весьма популярны, можно отметить использование шаблонов вывода данных. Шаблон может быть использован множество раз для форматирования списков данных переменной длины, выводимых в интерфейс. Взаимодействие представления с логикой приложения, которая находится на более "глубоких" уровнях MVVM, в частности – с моделью представления, обеспечивают механизмы привязки данных и команд.
Привязка данных. Привязка данных концептуально выглядит следующим образом. Имеется некий элемент управления, например – текстовое поле, в который мы хотим вывести некоторые данные. В XAML-коде, описывающем данное поле мы настраиваем связь этого поля с нужным нам свойством некоего объекта, который содержит данные, которые надо вывести в поле. Текстовое поле называют целевым объектом привязки или приемником, объект, содержащий данные, называют источником данных. Существуют разные типы привязки данных. В частности, так называемая односторонняя (one way) привязка данных приводит к тому, что информация берется из источника, направляется в приемник, при этом, если в приемнике происходит изменение данных, эти изменения не влияют на источник. Но если информация изменилась в источнике данных, изменения отразятся и на приемнике. При двусторонней привязке (two way) изменения, которым данные подвергнуться в приемнике, отражаются и на источнике данных. При этом описание привязок данных осуществляется в XAML-коде страницы, то есть, программный код для реализации этого механизма не нужен. Существуют и другие типы привязки, например, одноразовая (One time) – данные из источника передаются в приемник один раз, например, при смене контекста данных или при загрузке приложения, затем связь между ними не поддерживается. Передачу данных между источником и приемником выполняют механизмы системы привязки данных. К привязке данных относится такое понятие как конвертер данных – программный механизм, которые выполняет преобразование данных.
Привязка команд. Если из пользовательского интерфейса нужно произвести какое-либо управляющее воздействие, эту операцию реализуют с помощью привязки команд. Код команд определен в модели представления, а вызываются они из пользовательского интерфейса. В идеале, как и в случае с привязкой данных, привязка команд осуществляется в XAML-коде.
Модель представления. Модель представления связывает исходные данные, представленные моделью, и их визуальное представление. При этом модель представления не обладает "знаниями" о том, как именно данные используются в представлении – то есть – в интерфейсе приложения. Задача модели представления заключается в подготовке данных к отображению и в предоставлении этих данных представлению, то есть – интерфейсным механизмам – через систему привязки данных. То же самое касается команд. В модели представления может быть определен некий метод, который вызывается каким-либо элементом управления из представления, но этот метод не имеет информации о том, какой именно элемент управления его вызывает. Благодаря подобному разделению представления (интерфейса) и логики представления (модели представления) и достигается гибкость работы над различными частями приложения, созданного по методологии MVVM.
Модель. Модель ответственна за работу с исходными данными. Модель выполняет загрузку данных из внешних источников, их сохранение, при модификации в приложении, она предоставляет эти данные модели представления, которая приводит их к виду, подходящему для передачи представлению – то есть – для отображения в пользовательском интерфейсе.
При проектировании приложений с использованием шаблона MVVM очень важно правильно распределить функциональность приложения между его моделью, представлением и моделью представления.
Если рассматривать реализацию MVVM-приложения с использованием стандартных средств, доступных на платформе Windows Phone, то здесь можно отметить следующие особенности.
Рассмотрим реализацию некоторых вышеописанных принципов в приложении, построенном по шаблону Приложение Windows Phone с привязкой к данным.
Приложение, построенное с использованием данного шаблона, реализует основные принципы, характерные для шаблона MVVM. Нельзя сказать, что такое приложение в полной мере соответствует принципам MVVM. Например, модель и модель представления разделены недостаточно чётко (хотя, в данном случае можно говорить о том, что файл ItemViewModel, представляющий собой класс, определяющий структуру данных, которая используется в приложении, ближе к понятию "Model", чем к понятию "ViewModel", которое присутствует в его названии). В файлах программного кода страниц, которые формируют пользовательский интерфейс, производятся некоторые действия. Однако, есть в этом шаблоне и реализация основополагающих идей MVVM. В частности, он предусматривает использование данных периода проектирования, связь между исходными данными и представлением реализована с помощью системы привязки данных. В интерфейсе используются шаблоны, что закладывает основу для работы со списками данных переменной длины.
Рассмотрим приложение, созданное по этому шаблону, подробнее. На рис. 21.1. приведено окно Visual Studio, в котором развёрнута структура проекта, в визуальном конструкторе открыта страница MainPage.xaml.
(рис 21.1) Приложение, созданное по шаблону Приложение Windows Phone с привязкой к данным
Начнём рассмотрение проекта с папки SampleData. Здесь находится XAML-файл (Листинг 21.1), MainViewModelSampleData, который содержит данные периода проектирования. Структура данных, которые описаны в файле определяется классом ItemViewModel. В заголовке XAML-файла описано пространство имен vm, соответствующее пространству имен класса ItemViewModel. В файле содержится, кроме того, описание одиночного свойства из класса MainViewModel.
<vm:MainViewModel
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:L5_1.ViewModels"
SampleProperty="Пример значения свойства текста">
<vm:MainViewModel.Items>
<vm:ItemViewModel ID="0" LineOne="первая схема"
LineTwo="Maecenas praesent accumsan bibendum"
LineThree="Maecenas praesent accumsan bibendum
dictumst eleifend facilisi faucibus
habitant inceptos interdum lobortis
nascetur"/>
<vm:ItemViewModel ID="1" LineOne="вторая схема"
LineTwo="Dictumst eleifend facilisi faucibus"
LineThree="Pharetra placerat pulvinar sagittis
senectus sociosqu suscipit torquent
ultrices vehicula volutpat maecenas
praesent"/>
</vm:MainViewModel.Items>
</vm:MainViewModel>
Этот файл и представляет собой тот контекст данных, который назначен страницам приложения. При этом в режиме проектирования контекст данных для страницы MainPage (Листинг 21.2) соответствует общему списку данных, на этой странице выводится список элементов. А на странице DetailsPage (Листинг 21.3) выводится подробная информации об элементе с главной страницы, которого, при работе приложения, коснулся пользователь. В режиме проектирования контекст данных страницы так же установлен на вышеупомянутый файл, но контекст данных сетки LayoutRoot уточнён до элемента с индексом [0].
<phone:PhoneApplicationPage
x:Class="L5_1.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DataContext="{d:DesignData SampleData/MainViewModelSampleData.xaml}"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait" Orientation="Portrait"
shell:SystemTray.IsVisible="True">
<!--Для контекста данных установлен верхний пример данных, и LayoutRoot содержит корневую сетку, где размещается все содержимое страницы-->
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!--TitlePanel содержит имя приложения и заголовок страницы-->
<StackPanel Grid.Row="0" Margin="12,17,0,28">
<TextBlock Text="МОЕ ПРИЛОЖЕНИЕ" Style="{StaticResource PhoneTextNormalStyle}"/>
<TextBlock Text="имя страницы" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
</StackPanel>
<!--ContentPanel содержит LongListSelector и LongListSelector ItemTemplate. Поместите здесь дополнительное содержимое-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<phone:LongListSelector x:Name="MainLongListSelector" Margin="0,0,-12,0" ItemsSource="{Binding Items}"
SelectionChanged="MainLongListSelector_SelectionChanged">
<phone:LongListSelector.ItemTemplate>
<DataTemplate>
<StackPanel Margin="0,0,0,17">
<TextBlock Text="{Binding LineOne}" TextWrapping="Wrap" Style="{StaticResource PhoneTextExtraLargeStyle}"/>
<TextBlock Text="{Binding LineTwo}" TextWrapping="Wrap" Margin="12,-6,12,0"
Style="{StaticResource PhoneTextSubtleStyle}"/>
</StackPanel>
</DataTemplate>
</phone:LongListSelector.ItemTemplate>
</phone:LongListSelector>
</Grid>
</Grid>
</phone:PhoneApplicationPage>
Класс ItemViewModel, по сути, ближе всего к понятию "модели", принятом в MVVM. Он описывает базовую структуру данных, применяемых в программе. Эти данные применяются, во-первых, при заполнении страницы MainPage – на ней выводится список элементов, во-вторых – при заполнении страницы DetailsPage, которая содержит подробное описание одного элемента. Как уже было сказано, в режиме проектирования данные, выводимые на страницах, определены в файле MainViewModelSampleData.xaml. При запуске приложения контекст данных страницы MainPage (Листинг 21.4) устанавливается на объект типа MainViewModel, созданный в классе App (Листинг 21.5). Похожая процедура выполняется и при вызове страницы DetailsPage (Листинг 21.6). В XAML контекст данных установлен на один из элементов, описанных в MainViewModelSampleData.xaml, в режиме исполнения он программно устанавливается на один из элементов, присутствующих в объекте типа MainViewModel, который создаётся в классе App.
<phone:PhoneApplicationPage
x:Class="L5_1.DetailsPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
d:DataContext="{d:DesignData SampleData/MainViewModelSampleData.xaml}"
mc:Ignorable="d"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait" Orientation="Portrait"
shell:SystemTray.IsVisible="True">
<!--Для контекста данных установлен верхний пример данных и первый элемент из коллекции примеров данных ниже,
и LayoutRoot содержит корневую сетку, где размещается все содержимое страницы-->
<Grid x:Name="LayoutRoot" Background="Transparent" d:DataContext="{Binding Items[0]}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!--TitlePanel содержит имя приложения и заголовок страницы-->
<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
<TextBlock Text="МОЕ ПРИЛОЖЕНИЕ" Style="{StaticResource PhoneTextNormalStyle}"/>
<TextBlock Text="{Binding LineOne}" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
</StackPanel>
<!--ContentPanel содержит текст сведений. Поместите здесь дополнительное содержимое-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<TextBlock Text="{Binding LineThree}" TextWrapping="Wrap" Style="{StaticResource PhoneTextNormalStyle}"/>
</Grid>
</Grid>
</phone:PhoneApplicationPage>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Navigation;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Shell;
using L5_1.Resources;
using L5_1.ViewModels;
namespace L5_1
{
public partial class MainPage : PhoneApplicationPage
{
// Конструктор
public MainPage()
{
InitializeComponent();
// Задайте для контекста данных элемента управления LongListSelector пример данных
DataContext = App.ViewModel;
}
// Загрузка данных для элементов ViewModel
protected override void OnNavigatedTo(NavigationEventArgs e)
{
if (!App.ViewModel.IsDataLoaded)
{
App.ViewModel.LoadData();
}
}
// Обработка выбранных элементов, измененных в LongListSelector
private void MainLongListSelector_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
// Если выбранный элемент равен NULL (ничего не выбрано), никакие действия не требуются
if (MainLongListSelector.SelectedItem == null)
return;
// Переход на новую страницу
NavigationService.Navigate(new Uri("/DetailsPage.xaml?selectedItem=" +
(MainLongListSelector.SelectedItem as ItemViewModel).ID, UriKind.Relative));
// Сброс выбранного элемента в NULL (ничего не выбрано)
MainLongListSelector.SelectedItem = null;
}
}
}
using System;
using System.Diagnostics;
using System.Resources;
using System.Windows;
using System.Windows.Markup;
using System.Windows.Navigation;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Shell;
using L5_1.Resources;
using L5_1.ViewModels;
namespace L5_1
{
public partial class App : Application
{
private static MainViewModel viewModel = null;
/// <summary>
/// Статический элемент ViewModel, используемый в представлениях для привязки.
/// </summary>
/// <returns>Объект MainViewModel.</returns>
public static MainViewModel ViewModel
{
get
{
// Отложить создание модели представления до необходимости
if (viewModel == null)
viewModel = new MainViewModel();
return viewModel;
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Navigation;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Shell;
using L5_1.Resources;
namespace L5_1
{
public partial class DetailsPage : PhoneApplicationPage
{
// Конструктор
public DetailsPage()
{
InitializeComponent();
}
// При переходе на страницу установите для контекста данных выбранный элемент в списке
protected override void OnNavigatedTo(NavigationEventArgs e)
{
if (DataContext == null)
{
string selectedIndex = "";
if (NavigationContext.QueryString.TryGetValue("selectedItem", out selectedIndex))
{
int index = int.Parse(selectedIndex);
DataContext = App.ViewModel.Items[index];
}
}
}
}
}
Если говорить о классе ItemViewModel (Листинг 21.7), то он не связан с представлениями, он реализует интерфейс INotifyPropertyChanged, событие PropertyChanged вызывается при изменении свойств, которые описаны в классе.
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
namespace L5_1.ViewModels
{
public class ItemViewModel : INotifyPropertyChanged
{
private string _id;
/// <summary>
/// Пример свойства ViewModel; это свойство используется для идентификации объекта.
/// </summary>
/// <returns></returns>
public string ID
{
get
{
return _id;
}
set
{
if (value != _id)
{
_id = value;
NotifyPropertyChanged("ID");
}
}
}
private string _lineOne;
/// <summary>
/// Пример свойства ViewModel; это свойство используется в представлении для отображения его значения с помощью привязки.
/// </summary>
/// <returns></returns>
public string LineOne
{
get
{
return _lineOne;
}
set
{
if (value != _lineOne)
{
_lineOne = value;
NotifyPropertyChanged("LineOne");
}
}
}
private string _lineTwo;
/// <summary>
/// Пример свойства ViewModel; это свойство используется в представлении для отображения его значения с помощью привязки.
/// </summary>
/// <returns></returns>
public string LineTwo
{
get
{
return _lineTwo;
}
set
{
if (value != _lineTwo)
{
_lineTwo = value;
NotifyPropertyChanged("LineTwo");
}
}
}
private string _lineThree;
/// <summary>
/// Пример свойства ViewModel; это свойство используется в представлении для отображения его значения с помощью привязки.
/// </summary>
/// <returns></returns>
public string LineThree
{
get
{
return _lineThree;
}
set
{
if (value != _lineThree)
{
_lineThree = value;
NotifyPropertyChanged("LineThree");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (null != handler)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
В классе MainViewModel так же реализован INotifyPropertyChanged. Здесь событие PropertyChanged генерируется при изменении свойства SampleProperty. При этом класс содержит открытую коллекцию Items типа ItemViewModel – именно элементы этой коллекции, которая заполняется в этом же классе, и представляют собой те данные, которые при выполнении программы выводятся на главной странице в виде списка, и она же поставляет данные для страницы просмотра подробных сведений об элементе.
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using L5_1.Resources;
namespace L5_1.ViewModels
{
public class MainViewModel : INotifyPropertyChanged
{
public MainViewModel()
{
this.Items = new ObservableCollection<ItemViewModel>();
}
/// <summary>
/// Коллекция объектов ItemViewModel.
/// </summary>
public ObservableCollection<ItemViewModel> Items { get; private set; }
private string _sampleProperty = "Sample Runtime Property Value";
/// <summary>
/// Пример свойства ViewModel; это свойство используется в представлении
для отображения его значения с помощью привязки
/// </summary>
/// <returns></returns>
public string SampleProperty
{
get
{
return _sampleProperty;
}
set
{
if (value != _sampleProperty)
{
_sampleProperty = value;
NotifyPropertyChanged("SampleProperty");
}
}
}
/// <summary>
/// Пример свойства, возвращающего локализованную строку
/// </summary>
public string LocalizedSampleProperty
{
get
{
return AppResources.SampleProperty;
}
}
public bool IsDataLoaded
{
get;
private set;
}
/// <summary>
/// Создает и добавляет несколько объектов ItemViewModel в коллекцию элементов.
/// </summary>
public void LoadData()
{
// Пример данных; замените реальными данными
this.Items.Add(new ItemViewModel() { ID = "0", LineOne = "runtime one",
LineTwo = "Maecenas praesent accumsan bibendum",
LineThree = "Facilisi faucibus habitant inceptos interdum lobortis
nascetur pharetra placerat pulvinar sagittis senectus sociosqu" });
this.Items.Add(new ItemViewModel() { ID = "1", LineOne = "runtime two",
LineTwo = "Dictumst eleifend facilisi faucibus",
LineThree = "Suscipit torquent ultrices vehicula volutpat maecenas praesent accumsan
bibendum dictumst eleifend facilisi faucibus" });
this.IsDataLoaded = true;
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (null != handler)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
В результате использования в приложении, построенном по шаблону Приложение с привязкой данных, шаблона MVVM, мы, по сравнению с приложением, построенном, например, по шаблону Приложение для Windows Phone получаем некоторые преимущества. А именно – код – работающий с данными и презентационная логика приложения разделены. При создании интерфейса приложения мы получаем возможность работать с данными периода проектирования, которые позволяют при проектировании отобразить интерфейс, основанный на шаблоне (в частности, на странице MainPage) в виде, максимально приближенном к виду работающего приложения. И хотя этот шаблон не вполне соответствует принципам MVVM, использование подходов, примененных в нём, в проектах, которые предусматривают работу с некоторыми наборами данных значительно облегчает создание подобных проектов. Кроме того, освоение этого проекта позволяет переходить к освоению других инструментов и примеров приложений.
Нужно отметить, что приложения, построенные по шаблонам Приложение Windows Phone с панорамой и Приложение Windows Phone Pivot используют подход, похожий на тот, который применяется в шаблоне Приложение Windows Phone с привязкой к данным.
В качестве наглядной иллюстрации реализации концепции MVVM в приложениях для Windows Phone, можно привести пример "Model-View-ViewModel" (http://code.msdn.microsoft.com/wpapps/Model-View-ViewModel-Sample-8cb92fd9). Его можно либо загрузить по вышеуказанной ссылке, либо, в Visual Studio, в окне Создать проект перейти в группу В Интернете > Примеры. Здесь, в строке поиска, нужно ввести MVVM, что приведет к нахождению нескольких примеров, среди которых нас интересует "Model-View-ViewModel" (рис. 21.2), в данном случае – для Visual C#, тот же пример доступен и для Visual Basic.
(рис 21.2) Результаты поиска по примерам в окне Создать проект
Пример можно выбрать и создать с его помощью проект. Кроме того, для ознакомления с данным примером можно воспользоваться пошаговым руководством "Реализация шаблона MVVM в приложении для Windows Phone" (http://msdn.microsoft.com/en-us/library/windowsphone/develop/gg521153%28v=vs.105%29.aspx).
Для построения более совершенных, с точки зрения реализации MVVM, проектов, можно воспользоваться специальными наборами инструментов. Например, MVVM Light Toolkit (http://mvvmlight.codeplex.com/releases). На указанной странице проекта можно загрузить его установочный комплект, после установки которого и установки соответствующего VSIX-файла (инструкции по установке отображаются после инсталляции пакета) в списке C#-шаблонов приложений для Windows Phone появляется новый шаблон. MVVM Light доступен и через службу NuGet (Сервис > Диспетчер библиотек > Управление пакетами NuGet для решения).
Существуют и другие наборы инструментов, облегчающие и ускоряющие разработку с использованием шаблона проектирования MVVM.
В этой лекции мы рассмотрели основные понятия шаблона проектирования приложений MVVM, ознакомились с понятиями модели, представления, модели представления, привязки данных и команд. Применение данного шаблона позволяет упростить разработку сложных программных проектов за счёт чёткого разделения подсистем работы с данными и подсистем пользовательского интерфейса.
Проанализируйте идею приложения, которое вы разрабатываете, с точки зрения применения при его разработке шаблона проектирования MVVM. Разбейте логику приложения на модель, представление и модель представления, подготовьте отчёт о проделанной работе.
К данной лекции подготовлено видеоприложение и демонстрационный программный проект.
Цель работы: освоить основные понятия MVVM
Простейшее приложение для Windows Phone, всеми аспектами которого занимается один разработчик, вполне можно написать, не задумываясь о том, чтобы разделять код, имеющий отношение к визуализации данных, к их обработке и получению из некоторых источников. Приложение, в котором большая часть кода, ответственного за всю его функциональность, собрана в программном файле к его начальной странице (особенно, когда эта функциональность требует пары десятков строк кода), будет работать. Но небольшой рост сложности приложения приводит к тому, что таким кодом становится очень неудобно управлять. Его неудобно расширять, тестировать, неудобно привлекать других программистов к работе над приложением. Небольшое изменение в коде может повлечь за собой множество неожиданных, неприятных последствий. Идея разделения кода, который ответственен за работоспособность различных подсистем приложения, это одна из идей, которая легла в основу создания шаблонов проектирования приложений. Один из таких шаблонов, получивший популярность в последние годы, называется MVVM, Model-View-ViewModel, Модель-Представление-Модель представления. Этот шаблон используют на разных платформах, он универсален и позволяет создавать крупные программные проекты, обладающие чёткой и понятной структурой. Для того чтобы приступить к использованию MVVM, нужно понять сущность некоторых основных понятий, которые имеют к отношение к этому шаблону проектирования.
Очень кратко сущность MVVM можно выразить так: "Модель содержит исходные данные, которые готовятся к выводу с помощью модели представления и отображаются пользователю с помощью представления".
Рассмотрим это более подробно.
Представление (View). Это та часть приложения, которая формирует пользовательский интерфейс и отвечает за взаимодействие с пользователем. К представлению относится XAML-разметка страницы, отображаемые на странице элементы управления, анимации, которые применяются к элементам управления и логика, которая имеет отношение к интерфейсу. В идеальном варианте представление – это исключительно XAML-код, который описывает страницу и элементы управления, а в файле программного кода к этой странице содержится лишь конструктор, который вызывается при создании страницы. Для отображения интерфейса, однако, часто требуются какие-либо специфические операции, которые так же реализуют в файлах кода страниц. Представление, конечно, не используется автономно, никому не нужен интерфейс, который не отображает полезных данных и не способен обеспечить взаимодействие пользователя с этими данными. В том, что касается визуализации каких-либо списков данных, а подобные интерфейсы весьма популярны, можно отметить использование шаблонов вывода данных. Шаблон может быть использован множество раз для форматирования списков данных переменной длины, выводимых в интерфейс. Взаимодействие представления с логикой приложения, которая находится на более "глубоких" уровнях MVVM, в частности – с моделью представления, обеспечивают механизмы привязки данных и команд.
Привязка данных. Привязка данных концептуально выглядит следующим образом. Имеется некий элемент управления, например – текстовое поле, в который мы хотим вывести некоторые данные. В XAML-коде, описывающем данное поле мы настраиваем связь этого поля с нужным нам свойством некоего объекта, который содержит данные, которые надо вывести в поле. Текстовое поле называют целевым объектом привязки или приемником, объект, содержащий данные, называют источником данных. Существуют разные типы привязки данных. В частности, так называемая односторонняя (one way) привязка данных приводит к тому, что информация берется из источника, направляется в приемник, при этом, если в приемнике происходит изменение данных, эти изменения не влияют на источник. Но если информация изменилась в источнике данных, изменения отразятся и на приемнике. При двусторонней привязке (two way) изменения, которым данные подвергнуться в приемнике, отражаются и на источнике данных. При этом описание привязок данных осуществляется в XAML-коде страницы, то есть, программный код для реализации этого механизма не нужен. Существуют и другие типы привязки, например, одноразовая (One time) – данные из источника передаются в приемник один раз, например, при смене контекста данных или при загрузке приложения, затем связь между ними не поддерживается. Передачу данных между источником и приемником выполняют механизмы системы привязки данных. К привязке данных относится такое понятие как конвертер данных – программный механизм, которые выполняет преобразование данных.
Привязка команд. Если из пользовательского интерфейса нужно произвести какое-либо управляющее воздействие, эту операцию реализуют с помощью привязки команд. Код команд определен в модели представления, а вызываются они из пользовательского интерфейса. В идеале, как и в случае с привязкой данных, привязка команд осуществляется в XAML-коде.
Модель представления. Модель представления связывает исходные данные, представленные моделью, и их визуальное представление. При этом модель представления не обладает "знаниями" о том, как именно данные используются в представлении – то есть – в интерфейсе приложения. Задача модели представления заключается в подготовке данных к отображению и в предоставлении этих данных представлению, то есть – интерфейсным механизмам – через систему привязки данных. То же самое касается команд. В модели представления может быть определен некий метод, который вызывается каким-либо элементом управления из представления, но этот метод не имеет информации о том, какой именно элемент управления его вызывает. Благодаря подобному разделению представления (интерфейса) и логики представления (модели представления) и достигается гибкость работы над различными частями приложения, созданного по методологии MVVM.
Модель. Модель ответственна за работу с исходными данными. Модель выполняет загрузку данных из внешних источников, их сохранение, при модификации в приложении, она предоставляет эти данные модели представления, которая приводит их к виду, подходящему для передачи представлению – то есть – для отображения в пользовательском интерфейсе.
При проектировании приложений с использованием шаблона MVVM очень важно правильно распределить функциональность приложения между его моделью, представлением и моделью представления.
Если рассматривать реализацию MVVM-приложения с использованием стандартных средств, доступных на платформе Windows Phone, то здесь можно отметить следующие особенности.
Рассмотрим реализацию некоторых вышеописанных принципов в приложении, построенном по шаблону Приложение Windows Phone с привязкой к данным.
Приложение, построенное с использованием данного шаблона, реализует основные принципы, характерные для шаблона MVVM. Нельзя сказать, что такое приложение в полной мере соответствует принципам MVVM. Например, модель и модель представления разделены недостаточно чётко (хотя, в данном случае можно говорить о том, что файл ItemViewModel, представляющий собой класс, определяющий структуру данных, которая используется в приложении, ближе к понятию "Model", чем к понятию "ViewModel", которое присутствует в его названии). В файлах программного кода страниц, которые формируют пользовательский интерфейс, производятся некоторые действия. Однако, есть в этом шаблоне и реализация основополагающих идей MVVM. В частности, он предусматривает использование данных периода проектирования, связь между исходными данными и представлением реализована с помощью системы привязки данных. В интерфейсе используются шаблоны, что закладывает основу для работы со списками данных переменной длины.
Рассмотрим приложение, созданное по этому шаблону, подробнее. На рис. 21.1. приведено окно Visual Studio, в котором развёрнута структура проекта, в визуальном конструкторе открыта страница MainPage.xaml.
(рис 21.1) Приложение, созданное по шаблону Приложение Windows Phone с привязкой к данным
Начнём рассмотрение проекта с папки SampleData. Здесь находится XAML-файл (Листинг 21.1), MainViewModelSampleData, который содержит данные периода проектирования. Структура данных, которые описаны в файле определяется классом ItemViewModel. В заголовке XAML-файла описано пространство имен vm, соответствующее пространству имен класса ItemViewModel. В файле содержится, кроме того, описание одиночного свойства из класса MainViewModel.
<vm:MainViewModel
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:L5_1.ViewModels"
SampleProperty="Пример значения свойства текста">
<vm:MainViewModel.Items>
<vm:ItemViewModel ID="0" LineOne="первая схема"
LineTwo="Maecenas praesent accumsan bibendum"
LineThree="Maecenas praesent accumsan bibendum
dictumst eleifend facilisi faucibus
habitant inceptos interdum lobortis
nascetur"/>
<vm:ItemViewModel ID="1" LineOne="вторая схема"
LineTwo="Dictumst eleifend facilisi faucibus"
LineThree="Pharetra placerat pulvinar sagittis
senectus sociosqu suscipit torquent
ultrices vehicula volutpat maecenas
praesent"/>
</vm:MainViewModel.Items>
</vm:MainViewModel>
Этот файл и представляет собой тот контекст данных, который назначен страницам приложения. При этом в режиме проектирования контекст данных для страницы MainPage (Листинг 21.2) соответствует общему списку данных, на этой странице выводится список элементов. А на странице DetailsPage (Листинг 21.3) выводится подробная информации об элементе с главной страницы, которого, при работе приложения, коснулся пользователь. В режиме проектирования контекст данных страницы так же установлен на вышеупомянутый файл, но контекст данных сетки LayoutRoot уточнён до элемента с индексом [0].
<phone:PhoneApplicationPage
x:Class="L5_1.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DataContext="{d:DesignData SampleData/MainViewModelSampleData.xaml}"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait" Orientation="Portrait"
shell:SystemTray.IsVisible="True">
<!--Для контекста данных установлен верхний пример данных, и LayoutRoot содержит корневую сетку, где размещается все содержимое страницы-->
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!--TitlePanel содержит имя приложения и заголовок страницы-->
<StackPanel Grid.Row="0" Margin="12,17,0,28">
<TextBlock Text="МОЕ ПРИЛОЖЕНИЕ" Style="{StaticResource PhoneTextNormalStyle}"/>
<TextBlock Text="имя страницы" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
</StackPanel>
<!--ContentPanel содержит LongListSelector и LongListSelector ItemTemplate. Поместите здесь дополнительное содержимое-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<phone:LongListSelector x:Name="MainLongListSelector" Margin="0,0,-12,0" ItemsSource="{Binding Items}"
SelectionChanged="MainLongListSelector_SelectionChanged">
<phone:LongListSelector.ItemTemplate>
<DataTemplate>
<StackPanel Margin="0,0,0,17">
<TextBlock Text="{Binding LineOne}" TextWrapping="Wrap" Style="{StaticResource PhoneTextExtraLargeStyle}"/>
<TextBlock Text="{Binding LineTwo}" TextWrapping="Wrap" Margin="12,-6,12,0"
Style="{StaticResource PhoneTextSubtleStyle}"/>
</StackPanel>
</DataTemplate>
</phone:LongListSelector.ItemTemplate>
</phone:LongListSelector>
</Grid>
</Grid>
</phone:PhoneApplicationPage>
Класс ItemViewModel, по сути, ближе всего к понятию "модели", принятом в MVVM. Он описывает базовую структуру данных, применяемых в программе. Эти данные применяются, во-первых, при заполнении страницы MainPage – на ней выводится список элементов, во-вторых – при заполнении страницы DetailsPage, которая содержит подробное описание одного элемента. Как уже было сказано, в режиме проектирования данные, выводимые на страницах, определены в файле MainViewModelSampleData.xaml. При запуске приложения контекст данных страницы MainPage (Листинг 21.4) устанавливается на объект типа MainViewModel, созданный в классе App (Листинг 21.5). Похожая процедура выполняется и при вызове страницы DetailsPage (Листинг 21.6). В XAML контекст данных установлен на один из элементов, описанных в MainViewModelSampleData.xaml, в режиме исполнения он программно устанавливается на один из элементов, присутствующих в объекте типа MainViewModel, который создаётся в классе App.
<phone:PhoneApplicationPage
x:Class="L5_1.DetailsPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
d:DataContext="{d:DesignData SampleData/MainViewModelSampleData.xaml}"
mc:Ignorable="d"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait" Orientation="Portrait"
shell:SystemTray.IsVisible="True">
<!--Для контекста данных установлен верхний пример данных и первый элемент из коллекции примеров данных ниже,
и LayoutRoot содержит корневую сетку, где размещается все содержимое страницы-->
<Grid x:Name="LayoutRoot" Background="Transparent" d:DataContext="{Binding Items[0]}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!--TitlePanel содержит имя приложения и заголовок страницы-->
<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
<TextBlock Text="МОЕ ПРИЛОЖЕНИЕ" Style="{StaticResource PhoneTextNormalStyle}"/>
<TextBlock Text="{Binding LineOne}" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
</StackPanel>
<!--ContentPanel содержит текст сведений. Поместите здесь дополнительное содержимое-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<TextBlock Text="{Binding LineThree}" TextWrapping="Wrap" Style="{StaticResource PhoneTextNormalStyle}"/>
</Grid>
</Grid>
</phone:PhoneApplicationPage>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Navigation;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Shell;
using L5_1.Resources;
using L5_1.ViewModels;
namespace L5_1
{
public partial class MainPage : PhoneApplicationPage
{
// Конструктор
public MainPage()
{
InitializeComponent();
// Задайте для контекста данных элемента управления LongListSelector пример данных
DataContext = App.ViewModel;
}
// Загрузка данных для элементов ViewModel
protected override void OnNavigatedTo(NavigationEventArgs e)
{
if (!App.ViewModel.IsDataLoaded)
{
App.ViewModel.LoadData();
}
}
// Обработка выбранных элементов, измененных в LongListSelector
private void MainLongListSelector_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
// Если выбранный элемент равен NULL (ничего не выбрано), никакие действия не требуются
if (MainLongListSelector.SelectedItem == null)
return;
// Переход на новую страницу
NavigationService.Navigate(new Uri("/DetailsPage.xaml?selectedItem=" +
(MainLongListSelector.SelectedItem as ItemViewModel).ID, UriKind.Relative));
// Сброс выбранного элемента в NULL (ничего не выбрано)
MainLongListSelector.SelectedItem = null;
}
}
}
using System;
using System.Diagnostics;
using System.Resources;
using System.Windows;
using System.Windows.Markup;
using System.Windows.Navigation;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Shell;
using L5_1.Resources;
using L5_1.ViewModels;
namespace L5_1
{
public partial class App : Application
{
private static MainViewModel viewModel = null;
/// <summary>
/// Статический элемент ViewModel, используемый в представлениях для привязки.
/// </summary>
/// <returns>Объект MainViewModel.</returns>
public static MainViewModel ViewModel
{
get
{
// Отложить создание модели представления до необходимости
if (viewModel == null)
viewModel = new MainViewModel();
return viewModel;
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Navigation;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Shell;
using L5_1.Resources;
namespace L5_1
{
public partial class DetailsPage : PhoneApplicationPage
{
// Конструктор
public DetailsPage()
{
InitializeComponent();
}
// При переходе на страницу установите для контекста данных выбранный элемент в списке
protected override void OnNavigatedTo(NavigationEventArgs e)
{
if (DataContext == null)
{
string selectedIndex = "";
if (NavigationContext.QueryString.TryGetValue("selectedItem", out selectedIndex))
{
int index = int.Parse(selectedIndex);
DataContext = App.ViewModel.Items[index];
}
}
}
}
}
Если говорить о классе ItemViewModel (Листинг 21.7), то он не связан с представлениями, он реализует интерфейс INotifyPropertyChanged, событие PropertyChanged вызывается при изменении свойств, которые описаны в классе.
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
namespace L5_1.ViewModels
{
public class ItemViewModel : INotifyPropertyChanged
{
private string _id;
/// <summary>
/// Пример свойства ViewModel; это свойство используется для идентификации объекта.
/// </summary>
/// <returns></returns>
public string ID
{
get
{
return _id;
}
set
{
if (value != _id)
{
_id = value;
NotifyPropertyChanged("ID");
}
}
}
private string _lineOne;
/// <summary>
/// Пример свойства ViewModel; это свойство используется в представлении для отображения его значения с помощью привязки.
/// </summary>
/// <returns></returns>
public string LineOne
{
get
{
return _lineOne;
}
set
{
if (value != _lineOne)
{
_lineOne = value;
NotifyPropertyChanged("LineOne");
}
}
}
private string _lineTwo;
/// <summary>
/// Пример свойства ViewModel; это свойство используется в представлении для отображения его значения с помощью привязки.
/// </summary>
/// <returns></returns>
public string LineTwo
{
get
{
return _lineTwo;
}
set
{
if (value != _lineTwo)
{
_lineTwo = value;
NotifyPropertyChanged("LineTwo");
}
}
}
private string _lineThree;
/// <summary>
/// Пример свойства ViewModel; это свойство используется в представлении для отображения его значения с помощью привязки.
/// </summary>
/// <returns></returns>
public string LineThree
{
get
{
return _lineThree;
}
set
{
if (value != _lineThree)
{
_lineThree = value;
NotifyPropertyChanged("LineThree");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (null != handler)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
В классе MainViewModel так же реализован INotifyPropertyChanged. Здесь событие PropertyChanged генерируется при изменении свойства SampleProperty. При этом класс содержит открытую коллекцию Items типа ItemViewModel – именно элементы этой коллекции, которая заполняется в этом же классе, и представляют собой те данные, которые при выполнении программы выводятся на главной странице в виде списка, и она же поставляет данные для страницы просмотра подробных сведений об элементе.
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using L5_1.Resources;
namespace L5_1.ViewModels
{
public class MainViewModel : INotifyPropertyChanged
{
public MainViewModel()
{
this.Items = new ObservableCollection<ItemViewModel>();
}
/// <summary>
/// Коллекция объектов ItemViewModel.
/// </summary>
public ObservableCollection<ItemViewModel> Items { get; private set; }
private string _sampleProperty = "Sample Runtime Property Value";
/// <summary>
/// Пример свойства ViewModel; это свойство используется в представлении
для отображения его значения с помощью привязки
/// </summary>
/// <returns></returns>
public string SampleProperty
{
get
{
return _sampleProperty;
}
set
{
if (value != _sampleProperty)
{
_sampleProperty = value;
NotifyPropertyChanged("SampleProperty");
}
}
}
/// <summary>
/// Пример свойства, возвращающего локализованную строку
/// </summary>
public string LocalizedSampleProperty
{
get
{
return AppResources.SampleProperty;
}
}
public bool IsDataLoaded
{
get;
private set;
}
/// <summary>
/// Создает и добавляет несколько объектов ItemViewModel в коллекцию элементов.
/// </summary>
public void LoadData()
{
// Пример данных; замените реальными данными
this.Items.Add(new ItemViewModel() { ID = "0", LineOne = "runtime one",
LineTwo = "Maecenas praesent accumsan bibendum",
LineThree = "Facilisi faucibus habitant inceptos interdum lobortis
nascetur pharetra placerat pulvinar sagittis senectus sociosqu" });
this.Items.Add(new ItemViewModel() { ID = "1", LineOne = "runtime two",
LineTwo = "Dictumst eleifend facilisi faucibus",
LineThree = "Suscipit torquent ultrices vehicula volutpat maecenas praesent accumsan
bibendum dictumst eleifend facilisi faucibus" });
this.IsDataLoaded = true;
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (null != handler)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
В результате использования в приложении, построенном по шаблону Приложение с привязкой данных, шаблона MVVM, мы, по сравнению с приложением, построенном, например, по шаблону Приложение для Windows Phone получаем некоторые преимущества. А именно – код – работающий с данными и презентационная логика приложения разделены. При создании интерфейса приложения мы получаем возможность работать с данными периода проектирования, которые позволяют при проектировании отобразить интерфейс, основанный на шаблоне (в частности, на странице MainPage) в виде, максимально приближенном к виду работающего приложения. И хотя этот шаблон не вполне соответствует принципам MVVM, использование подходов, примененных в нём, в проектах, которые предусматривают работу с некоторыми наборами данных значительно облегчает создание подобных проектов. Кроме того, освоение этого проекта позволяет переходить к освоению других инструментов и примеров приложений.
Нужно отметить, что приложения, построенные по шаблонам Приложение Windows Phone с панорамой и Приложение Windows Phone Pivot используют подход, похожий на тот, который применяется в шаблоне Приложение Windows Phone с привязкой к данным.
В качестве наглядной иллюстрации реализации концепции MVVM в приложениях для Windows Phone, можно привести пример "Model-View-ViewModel" (http://code.msdn.microsoft.com/wpapps/Model-View-ViewModel-Sample-8cb92fd9). Его можно либо загрузить по вышеуказанной ссылке, либо, в Visual Studio, в окне Создать проект перейти в группу В Интернете > Примеры. Здесь, в строке поиска, нужно ввести MVVM, что приведет к нахождению нескольких примеров, среди которых нас интересует "Model-View-ViewModel" (рис. 21.2), в данном случае – для Visual C#, тот же пример доступен и для Visual Basic.
(рис 21.2) Результаты поиска по примерам в окне Создать проект
Пример можно выбрать и создать с его помощью проект. Кроме того, для ознакомления с данным примером можно воспользоваться пошаговым руководством "Реализация шаблона MVVM в приложении для Windows Phone" (http://msdn.microsoft.com/en-us/library/windowsphone/develop/gg521153%28v=vs.105%29.aspx).
Для построения более совершенных, с точки зрения реализации MVVM, проектов, можно воспользоваться специальными наборами инструментов. Например, MVVM Light Toolkit (http://mvvmlight.codeplex.com/releases). На указанной странице проекта можно загрузить его установочный комплект, после установки которого и установки соответствующего VSIX-файла (инструкции по установке отображаются после инсталляции пакета) в списке C#-шаблонов приложений для Windows Phone появляется новый шаблон. MVVM Light доступен и через службу NuGet (Сервис > Диспетчер библиотек > Управление пакетами NuGet для решения).
Существуют и другие наборы инструментов, облегчающие и ускоряющие разработку с использованием шаблона проектирования MVVM.
В этой лекции мы рассмотрели основные понятия шаблона проектирования приложений MVVM, ознакомились с понятиями модели, представления, модели представления, привязки данных и команд. Применение данного шаблона позволяет упростить разработку сложных программных проектов за счёт чёткого разделения подсистем работы с данными и подсистем пользовательского интерфейса.
Проанализируйте идею приложения, которое вы разрабатываете, с точки зрения применения при его разработке шаблона проектирования MVVM. Разбейте логику приложения на модель, представление и модель представления, подготовьте отчёт о проделанной работе.
К данной лекции подготовлено видеоприложение и демонстрационный программный проект.
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.