Пример MVVM (Model-View-ViewModel) или программирование на WPF (Silverlight)
WPF, MVVM, Silverlight | создано: 30.11.2010 | опубликовано: 22.04.2014 | обновлено: 26.06.2024 | просмотров: 45322 | всего комментариев: 13
Для того чтобы как можно проще рассказать о шаблоне MVVM (Model-View-ViewModel), который рекомендуется использовать при программировании на WPF (Silverlight). Приведу пример простого (ну, очень простого!) приложения.
Вступление
Очень просто. Есть список персон, у которых в скобках есть цифра (сначала подразумевалось, что это возраст). Есть детализированное представление записи и кнопки “увеличение” и “уменьшение” этого самой цифры (пусть это будет, всё-таки, возраст). Написание данного приложения заняло немногим около получаса. Безусловно, что “маленькие” проекты писать с использованием шаблона MVVM (Model-View-ViewModel) по меньшей мере нецелесообразно в силу временных затрат. Но я использовал этот шаблон программирования именно для того, чтобы на простом примере показать “что это такое?” и “с чем его едят?”.
Статья
Для того чтобы как можно проще рассказать о шаблоне MVVM (Model-View-ViewModel), который рекомендуется использовать при программировании на WPF (Silverlight). Приведу пример простого (ну, очень простого!) приложения.
Очень просто. Есть список персон, у которых в скобках есть цифра (сначала подразумевалось, что это возраст). Есть детализированное представление записи и кнопки “увеличение” и “уменьшение” этого самой цифры (пусть это будет, всё-таки, возраст). Написание данного приложения заняло немногим около получаса. Безусловно, что “маленькие” проекты писать с использованием шаблона MVVM (Model-View-ViewModel) по меньшей мере нецелесообразно в силу временных затрат. Но я использовал этот шаблон программирования именно для того, чтобы на простом примере показать “что это такое?” и “с чем его едят?”.
Как Вы уже наверное успели заметить, в проекте существует три папки: Model, View, ViewModel. Итак, для начала создадим файл ViewModelBase.cs, который станет прародителем некоторых классов.
public class ViewModelBase : INotifyPropertyChanged
{
public String DisplayName { get; set; }
#region INotifyPropertyChanged Members
protected void RaisePropertyChanged(string p)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(p));
}
}
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}
Этот класс реализует интерфейс INotifyPropertyChanged, чтобы было проще.
Далее покажу содержание файла Person.cs. Как Вы уже заметили это есть та самая модель данных, что, естественно, лежит в папке Model. Класс прост, поэтому описывать его не хочется, просто посмотрите. Вот она, модель:
/// Это модель для теста. Простой класс Person ///
public class Person : ViewModelBase {
private String firstName;
public String FirstName {
get { return firstName; }
set {
firstName = value;
base.RaisePropertyChanged("FirstName");
}
}
private String lastName;
public String LastName {
get { return lastName; }
set {
lastName = value;
base.RaisePropertyChanged("LastName");
}
} private Int32 age;
public Int32 Age {
get { return age; }
set {
age = value; base.RaisePropertyChanged("Age");
}
}
}
Следующим листингом будет файл PeopleViewModel.cs. А я остановлюсь на ключевых моментах. В силу того, что проект называется “простой”, то и данные мы будет получать статично. Вот конструктор класса:
public PeopleViewModel()
{
people = new ObservableCollection<Person>()
{
new Person() { Age = 23, FirstName = "Иван", LastName = "Иванов" },
new Person() { Age = 22, FirstName = "Петр", LastName = "Петров" },
new Person() { Age = 42, FirstName = "Сидор", LastName = "Сидоров" },
new Person() { Age = 36, FirstName = "Сергей", LastName = "Сергеев" },
new Person() { Age = 3, FirstName = "Анатолий", LastName = "Попов" }
};
}
Стоит также упомянуть о добавленных свойствах:
#region Properties
public Person SelectedPerson
{
get
{
return currentPerson;
}
set
{
if (currentPerson != value)
{
currentPerson = value;
RaisePropertyChanged("SelectedPerson");
}
}
}
public ObservableCollection<Person> People
{
get { return people; }
}
#endregion
Теперь пришло время показать, как же это всё привязывается к данным. Для этого заглянем в файл разметки PeopleViewer.xaml. Это один из самых “главных” файлов в этом самом простом приложении. Для начала обратите внимание на строки:
<UserControl.Resources>
<vm:PeopleViewModel x:Key="viewModel" />
<UserControl.Resources>
, в которых регистрируется ViewModel (не забудьте указать namespace).
xmlns:vm="clr-namespace:MVVMTest.ViewModel"
После того как ViewModel зарегистрирована, надо её быстренько отбиндить :) Как это делается, можно лицезреть
<Grid DataContext="{Binding Source={StaticResource viewModel}}">
. На представленном листинге видно что ListBox биндится свойству People, которое было создано специально для этого и являет собой ни что иное как ObservableCollection.
Обратите внимание на
<Grid x:Name="PersonDetails"
Grid.Row="0"
DataContext="{Binding SelectedPerson}"
Margin="5">
, в которой начинается описание еще одного Grid, в котором отображается детализированная информация. Свойство DataContext этого Grid также биндится но для этого уже используется другое свойство из нами созданных SelectedPerson.
И, наконец, про кнопки
<StackPanel Orientation="Horizontal"
HorizontalAlignment="Center"
Grid.Row="1">
<Button x:Name="button"
Content="-"
Width="32"
Height="32"
Command="{Binding DecreaseCommand}">
Button>
<Button x:Name="button1"
Content="+"
Width="32"
Height="32"
Command="{Binding IncreaseCommand}">
Button>
StackPanel>
Обратите внимание, как элегантно смотрится привязка (Binding). Вот теперь пришло время показать еще одну немаловажную часть шаблона MVVM.
Вот содержание класса RelayCommand.cs:
public class RelayCommand : ICommand
{
#region Fields
readonly Action<object> _execute;
readonly Predicate<object> _canExecute;
#endregion // Fields
#region Constructors
public RelayCommand(Action<object> execute)
: this(execute, null)
{
}
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute");
_execute = execute;
_canExecute = canExecute;
}
#endregion // Constructors
#region ICommand Members
[DebuggerStepThrough]
public bool CanExecute(object parameter)
{
return _canExecute == null ? true : _canExecute(parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameter)
{
_execute(parameter);
}
#endregion // ICommand Members
}
Хочу немного пояснить. На данный момент в WPF присутствует интерфейс ICommand, чего не скажешь о Silverlight. На момент написания статьи версия Silverlight имеет номер 3. И как утверждает Microsoft, 12 апреля 2010 года свет увидит четвертая версия Silverlight, где интерфейс ICommand уже будет реализован.
Осталось показать из чего состоит Window1.xaml, в котором представление и подключается (смотрите на строку номер 9):
<Window x:Class="MVVMTest.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:MVVMTest.View"
WindowStartupLocation="CenterScreen"
Title="Тест MVVM"
MinHeight="300"
MinWidth="300">
<vm:PeopleViewer />
</Window>
Хотелось бы отметить что Window1.xaml.cs и PeopleViewer.xaml.cs содержат только пустые конструкторы.
Подробнее о MVVM
Комментарии к статье (13)
Спасибо, простой и доходчивый пример. Но разве правильно наследовать Person от ViewModelBase? Это же модель, которая, по идее, не должна ни от чего зависеть.
Это просто название класса. А в классе реализация INotifyPropertyChanged
А где ссылка на файл проекта?
Илья, мне просто не хотелось что была привязка к конкретному фреймворку (MVVM Light, Prism или к моей собственной сборке) поэтому пример абстракно описывает концепцию.
Цитата:
Следующим листингом будет файл PeopleViewModel.cs. Целиком можно будет посмотреть скачав файл проекта (ссылка в конце статьи).
Ввела в заблуждение. :D
Поправил статью.
MVVM реализован неверно!
GooRoo, слишком громкое заявление для безосновательного выкрика! А где обоснования, примеры, подтверждения, доказательства? :)
mvvm реализован неверно так как у вас нет Model. Всё просто.
"...чтобы как можно проще рассказать о шаблоне MVVM..." Не вижу, чтобы о чём-то здесь рассказывалось. Статья в стиле: "Внимание, сейчас будет кусок кода: %кусок_кода%. А теперь - другой кусок кода: %другой_кусок_кода%." Информации, раскрывающей суть mvvm, в статье нет. Простой пример простого приложения.
Дмитрий,
> mvvm реализован неверно так как у вас нет Model. Всё просто."
Тогда что по-вашему Person собой представляет? И где указание на источник с правильной реализацией? :)
Altherial,
Разве в заголовке написано "MVVM - как паттерн проектирования: описание, история, авторы и т.д."? Кажется вы не очень внимательно вчитываетесь в материал, или некорректно трактуете содержимое. На мой взгляд, пример есть пример, а ссылки в начале статьи на описание паттерна достаточно для самостоятельного изучения.
Статья ГА.НО!!! Бесполезные куски кода