Тема: OnPropertyChanged, реализация MVP и MVVM
Цель: научиться реализовывать паттерны MVP и MVVM, понять разницу и освоить механизм уведомлений OnPropertyChanged.
Что нужно сделать:
✅ Чек-лист:
MainWindow.xaml и MainWindow.xaml.csЗадача: Создай интерфейс программы.
Открой MainWindow.xaml и замени содержимое на код ниже:
<Window x:Class="Lesson_MVP_MVVM.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Практика MVP/MVVM" Height="350" Width="450">
<StackPanel Margin="20">
<Label Content="Введите имя:" FontWeight="Bold"/>
<TextBox x:Name="InputTextBox" Margin="0,5" Padding="8"/>
<Label Content="Отображаемое имя:" FontWeight="Bold" Margin="0,15,0,0"/>
<TextBlock x:Name="DisplayTextBlock" Margin="0,5" Padding="8"
Background="#F0F0F0" FontSize="14" Height="35"/>
<Button x:Name="SetDefaultButton" Content="Установить 'Анна'"
Margin="0,20" Padding="10" Height="40"/>
</StackPanel>
</Window>
✏️ Твоё задание:
MainWindow.xamlMVP Без OnPropertyChanged. Presenter сам обновляет View.
Где создать: Правой кнопкой по проекту → Add → Class → имя IUserView.cs
namespace Lesson_MVP_MVVM
{
public interface IUserView
{
string InputName { get; set; }
string DisplayName { get; set; }
}
}
✏️ Задание: Создай файл и вставь код. Интерфейс описывает, что умеет View.
Где создать: Add → Class → UserModel.cs
namespace Lesson_MVP_MVVM
{
public class UserModel
{
private string _name = "Иван";
public string GetName() => _name;
public void SetName(string newName)
{
if (!string.IsNullOrWhiteSpace(newName))
_name = newName;
}
}
}
✏️ Задание: Создай модель. Она только хранит данные, ничего не знает о View.
Где создать: Add → Class → UserPresenter.cs
namespace Lesson_MVP_MVVM
{
public class UserPresenter
{
private IUserView _view;
private UserModel _model;
public UserPresenter(IUserView view)
{
_view = view;
_model = new UserModel();
_view.DisplayName = _model.GetName();
}
public void OnInputChanged()
{
_view.DisplayName = _view.InputName;
}
public void OnSetDefault()
{
_model.SetName("Анна");
_view.InputName = "Анна";
_view.DisplayName = "Анна";
}
}
}
✏️ Задание: Создай презентер. Он управляет логикой и обновляет View.
Открой MainWindow.xaml.cs и замени содержимое:
using System.Windows;
namespace Lesson_MVP_MVVM
{
public partial class MainWindow : Window, IUserView
{
private UserPresenter _presenter;
public string InputName
{
get => InputTextBox.Text;
set => InputTextBox.Text = value;
}
public string DisplayName
{
get => DisplayTextBlock.Text;
set => DisplayTextBlock.Text = value;
}
public MainWindow()
{
InitializeComponent();
_presenter = new UserPresenter(this);
InputTextBox.TextChanged += (s, e) => _presenter.OnInputChanged();
SetDefaultButton.Click += (s, e) => _presenter.OnSetDefault();
}
}
}
✅ Проверь себя (MVP):
MVVM С OnPropertyChanged. ViewModel не знает о View, только уведомляет через событие.
Создай новый проект (или очисти текущий от MVP-файлов).
Название: Lesson_MVVM
Или сделай копию проекта и удали IUserView, UserModel, UserPresenter.
Создай файл ViewModelBase.cs:
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace Lesson_MVVM
{
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler? PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
protected bool SetProperty<T>(ref T field, T value, [CallerMemberName] string? propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(field, value))
return false;
field = value;
OnPropertyChanged(propertyName);
return true;
}
}
}
✏️ Задание: Создай ViewModelBase. ВАЖНО Именно здесь живёт OnPropertyChanged.
Создай файл UserViewModel.cs:
namespace Lesson_MVVM
{
public class UserViewModel : ViewModelBase
{
private string _inputName = "";
private string _displayName = "Иван";
public string InputName
{
get => _inputName;
set
{
if (SetProperty(ref _inputName, value))
{
DisplayName = value;
}
}
}
public string DisplayName
{
get => _displayName;
set => SetProperty(ref _displayName, value);
}
public void SetDefaultName()
{
InputName = "Анна";
DisplayName = "Анна";
}
}
}
✏️ Задание: Создай ViewModel. Обрати внимание — нет никаких ссылок на View.
Открой MainWindow.xaml и замени содержимое:
<Window x:Class="Lesson_MVVM.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MVVM практика" Height="350" Width="450">
<StackPanel Margin="20">
<Label Content="Введите имя:" FontWeight="Bold"/>
<TextBox Text="{Binding InputName, UpdateSourceTrigger=PropertyChanged}"
Margin="0,5" Padding="8"/>
<Label Content="Отображаемое имя:" FontWeight="Bold" Margin="0,15,0,0"/>
<TextBlock Text="{Binding DisplayName}"
Margin="0,5" Padding="8"
Background="#F0F0F0" FontSize="14" Height="35"/>
<Button Content="Установить 'Анна'"
Click="SetDefaultButton_Click"
Margin="0,20" Padding="10" Height="40"/>
</StackPanel>
</Window>
✏️ Задание: Замени XAML. Обрати внимание на {Binding ...} — это привязки.
Открой MainWindow.xaml.cs и замени содержимое:
using System.Windows;
namespace Lesson_MVVM
{
public partial class MainWindow : Window
{
private UserViewModel _viewModel;
public MainWindow()
{
InitializeComponent();
_viewModel = new UserViewModel();
this.DataContext = _viewModel;
}
private void SetDefaultButton_Click(object sender, RoutedEventArgs e)
{
_viewModel.SetDefaultName();
}
}
}
✅ Проверь себя (MVVM):
OnPropertyChanged? (В SetProperty внутри ViewModelBase)Скопируй и заполни пропуски:
┌─────────────────────┬─────────────────────┬─────────────────────┐
│ │ MVP │ MVVM │
├─────────────────────┼─────────────────────┼─────────────────────┤
│ Используется │ ______ │ ______ │
│ OnPropertyChanged? │ │ │
├─────────────────────┼─────────────────────┼─────────────────────┤
│ Кто обновляет View? │ ______ │ View сама через │
│ │ │ Binding │
├─────────────────────┼─────────────────────┼─────────────────────┤
│ Знает ли Presenter/ │ ______ │ НЕТ │
│ ViewModel о View? │ │ │
├─────────────────────┼─────────────────────┼─────────────────────┤
│ Где хранится │ В IUserView и │ В XAML через │
│ логика обновления? │ UserPresenter │ {Binding} │
└─────────────────────┴─────────────────────┴─────────────────────┘
❓ Вопрос для размышления: Почему в MVVM нельзя написать textBox.Text = "значение"?
Ответ: ___________________________________________
Задача: Расширь MVVM-проект — добавь поле для ввода возраста и его отображение.
✏️ Что нужно сделать:
UserViewModel свойства InputAge и DisplayAge{Binding InputAge} и {Binding DisplayAge})SetDefaultName() сброс возраста на 25Критерии выполнения:
SetProperty и OnPropertyChangedВопрос 1. Что произойдёт, если в MVVM-классе не вызывать OnPropertyChanged?
Ответ: ___________________________________________
Вопрос 2. Зачем нужен [CallerMemberName] в методе OnPropertyChanged?
Ответ: ___________________________________________
Вопрос 3. Какую роль играет DataContext в MVVM?
Ответ: ___________________________________________
Вопрос 4. Почему в MVP не нужен OnPropertyChanged?
Ответ: ___________________________________________
Вопрос 5. Что делает метод SetProperty в ViewModelBase?
Ответ: ___________________________________________
Задача: Создать приложение для отображения списка студентов.
Требования:
📌 Подсказка: Для коллекций используй ObservableCollection<T> — она сама уведомляет UI об изменениях.
⭐ Бонус (+1 балл): Добавить сохранение и загрузку списка в JSON-файл.
| Раздел | Баллы |
|---|---|
| Часть 1-2 (MVP рабочий) | 3 балла |
| Часть 3-4 (MVVM рабочий) | 4 балла |
| Часть 5 (Самостоятельное задание) | 3 балла |
| Часть 6 (Контрольные вопросы) | 2 балла |
| Домашнее задание | 4 балла |
| Максимум | 16 баллов |
Проходной балл: 10 баллов
📌 Сохрани этот HTML-файл — он понадобится на следующем занятии.
Вопросы по заданию можно задать преподавателю.