Паттерн Model-View-ViewModel (MVVM) основывается на разделении функциональной части приложения на три ключевых компонента:
View - представление или пользовательский интерфейс
Model - модель или данные, которые используются в приложении
ViewModel - модель представления - промежуточный слой между представлением и данными, который обеспечивает их взаимодействие
Преимуществом использования данного паттерна является меньшая связанность между компонентами и разделение ответственности между ними. То есть Model отвечает за данные, View отвечает за графический интерфейс, а ViewModel - за логику приложения.
Наличие механизма привязки облегчает реализацию этого паттерна в Windows Forms.
Например, пусть у нас данные представлены следующим типом Person:
public class Person { public int Id { get; set; } public string Name { get; set; } = ""; public int Age { get; set; } public override string ToString() => Name; }
Данный класс представляет человека и имеет три свойства для хранения идентификатора, имени и возраста.
Определим класс модели представления ViewModel, который будет называться MainViewModel и будет иметь следующий код:
public class MainViewModel { // выбранный объект Person для отображения public Person? SelectedPerson { get => People.FirstOrDefault(p => p.Id == SelectedId); } // id выбранного объекта public int SelectedId { get; set; } // данные для отображения в списке public List<Person> People { get; } public MainViewModel() { People = new() { new Person {Id=1, Name="Tom", Age=38 }, new Person {Id=2, Name ="Bob", Age = 42}, new Person {Id=3, Name = "Sam", Age = 25} }; SelectedId = 2; // пусть по умолчанию выбран 2-й объект } }
Модель представления хранит данные для отображения - список People, который по умолчанию содержит 3 объекта Person.
Кроме того, определено свойство SelectedId
- идентификатор выбранного объекта.
Для отображения выбранного объекта также определено свойство SelectedPerson
, которое возвращает объект Person из списка People по выбранному Id.
Теперь создадим представление - определим следующую форму:
namespace MetanitApp; public partial class Form1 : Form { public Form1() { InitializeComponent(); // ListBox для отображения списка People ListBox peopleListBox= new ListBox(); peopleListBox.Dock= DockStyle.Fill; peopleListBox.SelectionMode = SelectionMode.One; Controls.Add(peopleListBox); // метка для отображения выбранного элемента Label selectedPersonLabel = new Label(); selectedPersonLabel.Margin = new Padding(20); selectedPersonLabel.Font = new Font(FontFamily.GenericSansSerif, 12); selectedPersonLabel.Dock = DockStyle.Top; Controls.Add(selectedPersonLabel); // в качестве констеста данных форма использует ViewModel DataContext = new MainViewModel(); // привязка свойства DataSource в ListBox к свойству People в MainViewModel peopleListBox.DataBindings.Add(new Binding("DataSource", DataContext, "People", false, DataSourceUpdateMode.OnPropertyChanged)); peopleListBox.DisplayMember = "Name"; peopleListBox.ValueMember= "Id"; // привязка свойства SelectedValue в ListBox к свойству SelectedId в MainViewModel peopleListBox.DataBindings.Add(new Binding("SelectedValue", DataContext, "SelectedId", false, DataSourceUpdateMode.OnPropertyChanged)); // привязка свойства Text в Label к свойству SelectedPerson в MainViewModel selectedPersonLabel.DataBindings.Add(new Binding("Text", DataContext, "SelectedPerson", false, DataSourceUpdateMode.OnPropertyChanged,"")); } }
Здесь в качестве контекста данных для формы и всех вложенных элементов устанавливается MainViewModel:
DataContext = new MainViewModel();
Далее мы можем установить привязки свойств элементов ListBox и Label к свойствам модели представления. Так, свойство DataSource
в ListBox привязано
к свойству People в MainViewModel:
peopleListBox.DataBindings.Add(new Binding("DataSource", DataContext, "People", false, DataSourceUpdateMode.OnPropertyChanged));
Поскольку в качестве свойства значения в ListBox применяется свойство Id объекта Person
peopleListBox.ValueMember= "Id";
то с помощью свойства SelectedValue
можно получить идентификатор выбранного элемента. И в данном случае мы устанавливаем привязку к свойству SelectedId в
MainViewModel:
peopleListBox.DataBindings.Add(new Binding("SelectedValue", DataContext, "SelectedId", false, DataSourceUpdateMode.OnPropertyChanged));
В конце для отслеживания выбранного элемента устанавливаем привязку свойства Text у элемента Label к свойству SelectedPerson модели представления.
Причем поскольку у нас потенциально может быть ситуация, когда не будет выбран объект, то есть он будет равен null
, то с помощью шестого параметра конструктора
Binding задаем значение по умолчанию на этот случай - здесь это пустая строка:
selectedPersonLabel.DataBindings.Add(new Binding("Text", DataContext, "SelectedPerson", false, DataSourceUpdateMode.OnPropertyChanged,""));
В итоге ListBox отобразит список объектов, а метка Label - выбранный объект: