Как связать элемент управления с ViewModel?

В Xamarin.Forms, чтобы связать элемент управления (UI-элемент) с ViewModel, используется механизм привязки данных (Data Binding). Основная идея заключается в том, чтобы UI (View) «слушал» свойства и команды, определённые во ViewModel. При изменении значений в ViewModel пользовательский интерфейс обновляется автоматически, и наоборот — при двусторонней привязке.

Процесс связывания элементов управления с ViewModel включает:

  1. Создание ViewModel с нужными свойствами и командами.

  2. Назначение ViewModel как BindingContext страницы или элемента.

  3. Настройку привязки в XAML или C# с использованием синтаксиса {Binding ...}.

1. Подготовка ViewModel

ViewModel — это обычный C#-класс, реализующий интерфейс INotifyPropertyChanged, который сообщает интерфейсу об изменениях в данных.

Пример ViewModel:

public class MainViewModel : INotifyPropertyChanged
{
private string userName;
public string UserName
{
get => userName;
set
{
if (userName != value)
{
userName = value;
OnPropertyChanged(nameof(UserName));
}
}
}
public ICommand SubmitCommand { get; }
public MainViewModel()
{
SubmitCommand = new Command(OnSubmit);
}
private void OnSubmit()
{
// Логика обработки
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

2. Установка BindingContext

BindingContext — это объект, свойства которого будут использоваться в выражениях привязки. Обычно это экземпляр ViewModel.

Установка в C#:

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

Установка в XAML:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:local="clr-namespace:MyApp.ViewModels"
x:Class="MyApp.MainPage">
&lt;ContentPage.BindingContext&gt;
&lt;local:MainViewModel /&gt;
&lt;/ContentPage.BindingContext&gt;

3. Привязка свойств UI-элемента к ViewModel

Теперь можно привязать конкретные свойства элементов управления к свойствам ViewModel, используя синтаксис {Binding PropertyName}.

Пример: Entry (ввод текста) и Label (отображение)

ViewModel:

public string UserName { get; set; }

XAML:

&lt;Entry Text="{Binding UserName, Mode=TwoWay}" Placeholder="Введите имя" /&gt;
&lt;Label Text="{Binding UserName}" /&gt;
  • Mode=TwoWay позволяет как получать значение, так и отправлять его обратно из UI в ViewModel.

4. Привязка команд (ICommand)

Чтобы обработать действия пользователя, например нажатие кнопки, можно привязать кнопку к команде из ViewModel.

ViewModel:

public ICommand SubmitCommand { get; }
public MainViewModel()
{
SubmitCommand = new Command(OnSubmit);
}

XAML:

&lt;Button Text="Отправить" Command="{Binding SubmitCommand}" /&gt;

5. Привязка коллекции к списковому элементу (ListView, CollectionView)

Для отображения списков объектов из ViewModel используется ObservableCollection.

ViewModel:

public ObservableCollection&lt;string&gt; Items { get; }
public MainViewModel()
{
Items = new ObservableCollection&lt;string&gt;
{
"Элемент 1",
"Элемент 2",
"Элемент 3"
};
}

XAML:

&lt;ListView ItemsSource="{Binding Items}"&gt;
&lt;ListView.ItemTemplate&gt;
&lt;DataTemplate&gt;
&lt;TextCell Text="{Binding .}" /&gt;
&lt;/DataTemplate&gt;
&lt;/ListView.ItemTemplate&gt;
&lt;/ListView&gt;

{Binding .} — означает привязку к самому элементу коллекции, если он простого типа, например string.

6. Использование BindingContext на уровне компонентов

BindingContext можно задавать не только на уровне страницы, но и у отдельных элементов или layout-контейнеров.

Пример:

&lt;StackLayout BindingContext="{Binding CurrentUser}"&gt;
&lt;Label Text="{Binding Name}" /&gt;
&lt;Label Text="{Binding Email}" /&gt;
&lt;/StackLayout&gt;

В этом случае Name и Email должны быть свойствами объекта CurrentUser, который, в свою очередь, является свойством ViewModel.

7. Привязка к ресурсам и конвертерам

Если нужно изменить отображаемое значение (например, логическое значение → цвет), можно использовать IValueConverter.

Пример конвертера:

public class BoolToColorConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (bool)value ? Color.Green : Color.Red;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return Binding.DoNothing;
}
}

Ресурс в XAML:

&lt;ContentPage.Resources&gt;
&lt;ResourceDictionary&gt;
&lt;local:BoolToColorConverter x:Key="BoolToColorConverter" /&gt;
&lt;/ResourceDictionary&gt;
&lt;/ContentPage.Resources&gt;

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

<Label Text="Статус"
TextColor="{Binding IsActive, Converter={StaticResource BoolToColorConverter}}" />

8. Проверка правильности привязки

Иногда элемент не отображает данные, даже если привязка указана. Чтобы устранить ошибки:

  • Убедитесь, что свойство BindingContext установлено правильно.

  • Проверьте, что имена свойств указаны корректно (учитывается регистр).

  • Убедитесь, что свойства ViewModel реализуют уведомления (INotifyPropertyChanged).

  • Для ListView и CollectionView используйте ObservableCollection.

  • Включите отладку привязки в Output (Visual Studio → Output → Xamarin Output).

9. Пример полной привязки элемента к ViewModel

ViewModel:

public class LoginViewModel : INotifyPropertyChanged
{
private string username;
public string Username
{
get => username;
set
{
username = value;
OnPropertyChanged(nameof(Username));
}
}
private string password;
public string Password
{
get => password;
set
{
password = value;
OnPropertyChanged(nameof(Password));
}
}
public ICommand LoginCommand { get; }
public LoginViewModel()
{
LoginCommand = new Command(OnLogin);
}
private void OnLogin()
{
// Авторизация
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

XAML:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:local="clr-namespace:MyApp.ViewModels"
x:Class="MyApp.LoginPage">
&lt;ContentPage.BindingContext&gt;
&lt;local:LoginViewModel /&gt;
&lt;/ContentPage.BindingContext&gt;
&lt;StackLayout Padding="20"&gt;
&lt;Entry Placeholder="Логин" Text="{Binding Username}" /&gt;
&lt;Entry Placeholder="Пароль" Text="{Binding Password}" IsPassword="True" /&gt;
&lt;Button Text="Войти" Command="{Binding LoginCommand}" /&gt;
&lt;/StackLayout&gt;
&lt;/ContentPage&gt;

10. Привязка в C# (без XAML)

Иногда вся страница создаётся на C#, без XAML. В этом случае привязка тоже возможна.

Пример:

var entry = new Entry();
entry.SetBinding(Entry.TextProperty, "Username");
var button = new Button { Text = "Войти" };
button.SetBinding(Button.CommandProperty, "LoginCommand");
Content = new StackLayout
{
Children = { entry, button }
};
BindingContext = new LoginViewModel();

Связывание элементов управления с ViewModel в Xamarin.Forms — ключевой механизм построения реактивного, масштабируемого интерфейса. Он минимизирует необходимость писать код вручную, обеспечивая чистую архитектуру и лёгкую поддержку при росте функциональности приложения.