Как происходит взаимодействие между View и ViewModel?

Во взаимодействии между View (представлением) и ViewModel (моделью представления) в Xamarin.Forms основную роль играет паттерн MVVM (Model-View-ViewModel), который обеспечивает четкое разделение UI от логики. Это повышает масштабируемость, модульность, тестируемость и переиспользуемость кода. Основным механизмом, который обеспечивает связку между View и ViewModel, является привязка данных (Data Binding).

Общая схема взаимодействия

  1. View — XAML или код, отображающий пользовательский интерфейс. Не содержит бизнес-логики.

  2. ViewModel — класс, содержащий свойства, команды и обработчики, к которым привязывается View.

  3. Model — уровень данных, бизнес-логика, работа с БД и API (не участвует напрямую в отображении).

Связь между View и ViewModel осуществляется односторонне или двусторонне через Data Binding, что позволяет View автоматически получать изменения из ViewModel и наоборот (если нужно).

Связывание View с ViewModel

1. Через свойство BindingContext

Присваивая ViewModel в BindingContext, устанавливается источник данных для всех привязок внутри View.

Пример в C#:

public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
BindingContext = new MainViewModel();
}
}

Пример в XAML:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:vm="clr-namespace:MyApp.ViewModels"
x:Class="MyApp.MainPage">
&lt;ContentPage.BindingContext&gt;
&lt;vm:MainViewModel /&gt;
&lt;/ContentPage.BindingContext&gt;
&lt;StackLayout&gt;
&lt;Label Text="{Binding Title}" /&gt;
&lt;Button Text="Клик" Command="{Binding ClickCommand}" /&gt;
&lt;/StackLayout&gt;
&lt;/ContentPage&gt;

Привязка свойств (Property Binding)

Односторонняя (OneWay)

&lt;Label Text="{Binding Title}" /&gt;

UI обновляется, если Title изменяется. Для этого ViewModel должно реализовать INotifyPropertyChanged.

Двусторонняя (TwoWay)

&lt;Entry Text="{Binding UserName, Mode=TwoWay}" /&gt;

Используется при вводе данных — изменения в Entry передаются в ViewModel, и наоборот.

Реализация ViewModel: INotifyPropertyChanged

Для того чтобы View знала об изменениях в свойствах ViewModel, она должна реализовать интерфейс INotifyPropertyChanged.

Пример:

public class MainViewModel : INotifyPropertyChanged
{
private string \_title;
public string Title
{
get => \_title;
set
{
if (\_title == value) return;
\_title = value;
OnPropertyChanged(nameof(Title));
}
}

public event PropertyChangedEventHandler PropertyChanged;

protected virtual void OnPropertyChanged(string name) =>

PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));

}

Команды (ICommand) — взаимодействие с действиями UI

Позволяют ViewModel обрабатывать события без необходимости писать обработчики событий в View.

Пример:

public ICommand ClickCommand { get; }
public MainViewModel()
{
ClickCommand = new Command(OnClicked);
}
private void OnClicked()
{
Title = "Нажато!";
}

В XAML:

&lt;Button Text="Нажми меня" Command="{Binding ClickCommand}" /&gt;

Если нужно передать параметры — используется CommandParameter.

Обработка событий в ViewModel через EventToCommand (поведение)

Xamarin.Forms не поддерживает прямую привязку к событиям, но можно использовать поведение (Behavior):

&lt;Button Text="Кнопка"&gt;
&lt;Button.Behaviors&gt;
<behaviors:EventToCommandBehavior EventName="Clicked"
Command="{Binding MyCommand}" />
&lt;/Button.Behaviors&gt;
&lt;/Button&gt;

View обновляется при изменениях в ViewModel

  1. Свойства ViewModel обновляются (например, в результате API-запроса).

  2. Вызывается OnPropertyChanged, View автоматически получает обновленное значение.

  3. UI мгновенно реагирует без необходимости вручную обновлять элементы.

ViewModel получает данные от View

Ввод текста

&lt;Entry Text="{Binding Email, Mode=TwoWay}" /&gt;

Пользователь вводит email — значение передается в свойство Email в ViewModel.

Выбор в Picker

<Picker ItemsSource="{Binding Cities}"
SelectedItem="{Binding SelectedCity}" />

SelectedCity обновляется в ViewModel при выборе пользователем.

Navigation из ViewModel

С помощью внедрения INavigation или через сервис навигации (например, INavigationService):

public INavigation Navigation { get; set; }
public async Task OpenDetailsPage()
{
await Navigation.PushAsync(new DetailsPage());
}

Или при использовании DI:

public class MyViewModel
{
private readonly INavigationService \_nav;
public MyViewModel(INavigationService nav)
{
\_nav = nav;
}
public async Task GoNext() => await \_nav.NavigateToAsync&lt;DetailsViewModel&gt;();
}

Пример полного взаимодействия View и ViewModel

ViewModel:

public class LoginViewModel : INotifyPropertyChanged
{
private string \_username;
public string Username
{
get => \_username;
set { \_username = value; OnPropertyChanged(nameof(Username)); }
}
public ICommand LoginCommand { get; }
public LoginViewModel()
{
LoginCommand = new Command(OnLogin);
}
private void OnLogin()
{
// логика авторизации
}
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged(string name) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}

View (XAML):

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:vm="clr-namespace:MyApp.ViewModels"
x:Class="MyApp.LoginPage">
&lt;ContentPage.BindingContext&gt;
&lt;vm:LoginViewModel /&gt;
&lt;/ContentPage.BindingContext&gt;
&lt;StackLayout Padding="20"&gt;
<Entry Placeholder="Имя пользователя"
Text="{Binding Username, Mode=TwoWay}" />
<Button Text="Войти"
Command="{Binding LoginCommand}" />
&lt;/StackLayout&gt;
&lt;/ContentPage&gt;

Практические замечания

  • Все ViewModel желательно наследовать от базового класса, реализующего INotifyPropertyChanged.

  • Для Unit-тестов легко заменять зависимости ViewModel на моки.

  • Не следует использовать code-behind в View — вся логика должна быть в ViewModel.

  • Если ViewModel сложная, стоит разделить на части (например, по вкладкам или экранам).

  • Команды должны быть реализованы через Command или AsyncCommand.

Во взаимодействии View и ViewModel основное правило — UI ничего не должен "знать" о логике, а ViewModel не должен зависеть от UI. Это обеспечивает высокую модульность, переиспользуемость и простоту поддержки приложения. Связь между слоями обеспечивается Data Binding, командами и шаблонами событий, что позволяет разрабатывать масштабируемые и чистые архитектурные приложения в Xamarin.Forms.