Команды и взаимодействие с пользователем в MVVM

Последнее обновление: 13.01.2021

В прошлой теме описывался паттерн MVVM, позволяющий отображать связанные данные. Однако, как правило, необходимо не только отображать данные, но и использовать какую-то логику взаимодействия с пользователем, обрабатывать пользовательский ввод. Рассмотрим, как это сделать в рамках паттерна MVVM.

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

Механизм команд раскрывается через интерфейс ICommand:

public interface ICommand 
{ 
	void Execute(object arg); 
	bool CanExecute(object arg); 
	event EventHandler CanExecuteChanged; 
}

Интерфейс ICommand определен в пространстве имен System.Windows.Input и расположен в сборке System.ObjectModel.

Встроенную поддержку команд имеют следующие элементы управления:

  • Button

  • MenuItem

  • ToolbarItem

  • SearchBar

  • TextCell

  • ImageCell

  • ListView

  • TapGestureRecognizer

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

using System;
using System.Windows.Input;

namespace HelloApp
{
    public class IncreasePriceCommand : ICommand
    {
        public event EventHandler CanExecuteChanged;
        PhoneViewModel viewModel;
        public IncreasePriceCommand(PhoneViewModel vm)
        {
            viewModel = vm;
        }

        public bool CanExecute(object parameter)
        {
            return viewModel.Price < 100000;
        }

        public void Execute(object parameter)
        {
            if (CanExecute(parameter))
                viewModel.IncreasePrice();
        }
    }
}

Команда через конструктор принимает объект PhoneViewModel. Метод CanExecute возвращает true, если у цена во viewModel меньше 100000. Здесь можно определить любое условие. В прицнипе данный метод вообще не обязательно использовать.

Метод Execute() собственно выполняет действие команды. В нашем случае будет вызываться метод IncreasePrice, который мы определим в ViewModel. Стоит отметить, что метод принимает параметр типа object, то есть мы можем извне передавать в команду различные данные.

Теперь изменим класс PhoneViewModel, чтобы он использовал данную команду:

using System.ComponentModel;
using System.Windows.Input;

namespace HelloApp
{
    public class PhoneViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        private Phone phone;
        public ICommand Increase { get; }

        public PhoneViewModel()
        {
           phone = new Phone();
            Increase = new IncreasePriceCommand(this);
        }

        public void IncreasePrice()
        {
            if (phone != null)
                Price += 100;
        }

        public string Title
        {
            get { return phone.Title; }
            set
            {
                if (phone.Title != value)
                {
                    phone.Title = value;
                    OnPropertyChanged("Title");
                }
            }
        }
        public string Company
        {
            get { return phone.Company; }
            set
            {
                if (phone.Company != value)
                {
                    phone.Company = value;
                    OnPropertyChanged("Company");
                }
            }
        }
        public int Price
        {
            get { return phone.Price; }
            set
            {
                if (phone.Price != value)
                {
                    phone.Price = value;
                    OnPropertyChanged("Price");
                }
            }
        }
        protected void OnPropertyChanged(string propName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propName));
        }
    }
}

ViewModel содержит команду Increase, которая представляет тип IncreasePriceCommand.

И также изменим код страницы в xaml, добавив в него кнопки с привязкой к вышеопределенным командам:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="HelloApp.MainPage">
    <StackLayout>
        <Label Text="{Binding Title}" FontSize="Medium" />
        <Label Text="{Binding Company}" FontSize="Medium" />
        <Label Text="{Binding Price}" FontSize="Medium" />
        <Button Text="+" Command="{Binding Increase}" />
    </StackLayout>
</ContentPage>

С помощью свойства Command и выражения привязки кнопка связывается с командой Increase. И при нажатии на кнопку происходит действие, заложенное в связанной команде, то есть произойдет увеличение цены товара.

Команды в Xamarin Forms

Также стоит отметить, что если мы хотим использовать команды, то нам необязательно определять новый класс команды. В Xamarin.Forms есть встроенный класс Command, который реализует всю функциональность. Нам же достаточно передать в его конструктор нужно действие, которое соответствует делегату Action. Например, изменим класс PhoneViewModel следующим образом:

using System.ComponentModel;
using System.Windows.Input;
using Xamarin.Forms;

namespace HelloApp
{
    public class PhoneViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        private Phone phone;
        public ICommand Increase { get; }

        public PhoneViewModel()
        {
           phone = new Phone();
            Increase = new Command(IncreasePrice);
        }

        public void IncreasePrice()
        {
            if (phone != null)
                Price += 100;
        }

        public string Title
        {
            get { return phone.Title; }
            set
            {
                if (phone.Title != value)
                {
                    phone.Title = value;
                    OnPropertyChanged("Title");
                }
            }
        }
        public string Company
        {
            get { return phone.Company; }
            set
            {
                if (phone.Company != value)
                {
                    phone.Company = value;
                    OnPropertyChanged("Company");
                }
            }
        }
        public int Price
        {
            get { return phone.Price; }
            set
            {
                if (phone.Price != value)
                {
                    phone.Price = value;
                    OnPropertyChanged("Price");
                }
            }
        }
        protected void OnPropertyChanged(string propName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propName));
        }
    }
}

И приложение будет также работать.

Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850