Xamarin.Forms Кнопки

Автор:

www.microsoft.com

Кнопка реагирует на нажатие или щелчок, который направляет приложение на выполнение определенной задачи.

Кнопка (Button) – это основной интерактивный элемент управления в Xamarin.Forms. Кнопка в Xamarin обычно отображает короткую текстовую строку, указывающую на команду, но она также может отображать растровое изображение или комбинацию текста и изображения. Пользователь нажимает на кнопку пальцем или щелкает по ней мышью, чтобы инициировать выполнение команды.

Xamarin: обработка нажатий на кнопку

Button определяет событие Clicked, которое запускается, когда пользователь касается кнопки пальцем или указателем мыши. Событие срабатывает, когда палец или кнопка мыши отпускается с поверхности кнопки. Чтобы кнопка реагировала на нажатия, ее свойство IsEnabled должно быть установлено в true.

Пример файла BasicButtonClickPage.xaml, который содержит макет StackLayout с меткой и кнопкой:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
            xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
            x:Class="ButtonDemos.BasicButtonClickPage"
            Title="Basic Button Click">
   <StackLayout>
 
       <Label x:Name="label"
              Text="Click the Button below"
              FontSize="Large"
              VerticalOptions="CenterAndExpand"
              HorizontalOptions="Center" />
 
       <Button Text="Click to Rotate Text!"
               VerticalOptions="CenterAndExpand"
               HorizontalOptions="Center"
               Clicked="OnButtonClicked" />
 
   </StackLayout>
</ContentPage>

Элемент Кнопка, если не задать правильные свойства, может занять все пространство, которое определено под ее размещение. Например, если вы не установите свойство HorizontalOptions, отличное от Fill, кнопка будет занимать всю ширину родительского элемента.

По умолчанию кнопка имеет прямоугольную форму, но вы можете придать ей закругленные углы с помощью свойства CornerRadius. Свойство Text задает текст, который отображается в кнопке. Событие Clicked устанавливается на обработчик события OnButtonClicked. Этот обработчик находится в файле code-behind, BasicButtonClickPage.xaml.cs:

C#
public partial class BasicButtonClickPage : ContentPage
{
   public BasicButtonClickPage ()
   {
       InitializeComponent ();
   }
 
   async void OnButtonClicked(object sender, EventArgs args)
   {
       await label.RelRotateTo(360, 1000);
   }
}
 

Когда кнопка нажата, выполняется метод OnButtonClicked. Аргументом sender является объект Button, ответственный за это событие. Его можно использовать для доступа к объекту Button или для различения нескольких объектов Button, совместно использующих одно и то же событие Clicked.

Этот конкретный обработчик Clicked вызывает функцию анимации, которая поворачивает Label на 360 градусов за 1000 миллисекунд. Вот программа, запущенная на устройствах iOS и Android, а также как приложение Universal Windows Platform (UWP) на рабочем столе Windows 10:

Обратите внимание, что метод OnButtonClicked включает модификатор async, поскольку в обработчике события используется await. Обработчик события Clicked требует модификатора async, только если в теле обработчика используется await.

Каждая платформа отображает кнопку по-своему. В разделе «Внешний вид кнопки» вы увидите, как задать цвета и сделать границу кнопки видимой для более индивидуального отображения. Button реализует интерфейс IFontElement, поэтому она включает свойства FontFamily, FontSize и FontAttributes.

Создание кнопки в коде

Обычно кнопка создается в XAML, но вы также можете создать ее в коде. Для разработчиков этот вариант может быть более удобным, если необходимо создать несколько кнопок на основе данных, перечисляемых с помощью цикла foreach.

Пример, как создать страницу, функционально эквивалентную странице Basic Button Click, но полностью на C#:

public class CodeButtonClickPage : ContentPage
{
   public CodeButtonClickPage ()
   {
       Title = "Code Button Click";
 
       Label label = new Label
       {
           Text = "Click the Button below",
           FontSize = Device.GetNamedSize(NamedSize.Large, typeof(Label)),
           VerticalOptions = LayoutOptions.CenterAndExpand,
           HorizontalOptions = LayoutOptions.Center
       };
 
       Button button = new Button
       {
           Text = "Click to Rotate Text!",
           VerticalOptions = LayoutOptions.CenterAndExpand,
           HorizontalOptions = LayoutOptions.Center
       };
       button.Clicked += async (sender, args) => await label.RelRotateTo(360, 1000);
 
       Content = new StackLayout
       {
           Children =
           {
               label,
               button
           }
       };
   }
}

Все делается в конструкторе класса. Поскольку обработчик Clicked состоит всего из одного оператора, он может быть присоединен к событию очень просто:

button.Clicked += async (sender, args) => await label.RelRotateTo(360, 1000);

Конечно, вы также можете определить обработчик события как отдельный метод (подобно методу OnButtonClick в Basic Button Click) и присоединить этот метод к событию:

button.Clicked += OnButtonClicked;

Отключение кнопки

Иногда в разрабатываемом приложении нажатие определенной кнопки не является допустимой операцией. В таких случаях кнопку следует отключить, установив для ее свойства IsEnabled значение false. Классический пример – элемент управления Entry для ввода имени файла, сопровождаемый кнопкой открытия файла: Кнопка (Button) должна быть включена только в том случае, если в поле ввода был набран текст. Для этой задачи можно использовать, например, триггер данных (DataTrigger).

Использование командного интерфейса

Приложение может реагировать на нажатия кнопки (Button) без обработки события Clicked. Кнопка (Button) реализует альтернативный механизм уведомления, называемый интерфейсом команд или командным интерфейсом. Он состоит из двух свойств:

  • Command типа ICommand, интерфейс, определенный в пространстве имен System.Windows.Input;
  • Свойство CommandParameter типа Object.

Этот метод особенно подходит для связи с привязкой данных, и особенно при реализации архитектуры Model-View-ViewModel (MVVM).

В MVVM-приложении модель представления определяет свойства типа ICommand, которые затем подключаются к элементам XAML Button с помощью привязки данных. Xamarin.Forms также определяет классы Command и Command, которые реализуют интерфейс ICommand и помогают модели представления определять свойства типа ICommand.

Класс CommandDemoViewModel – это очень простая модель представления, которая определяет свойство типа double под названием Number и два свойства типа ICommand под названием MultiplyBy2Command и DivideBy2Command:

class CommandDemoViewModel : INotifyPropertyChanged
{
   double number = 1;
 
   public event PropertyChangedEventHandler PropertyChanged;
 
   public CommandDemoViewModel()
   {
       MultiplyBy2Command = new Command(() => Number *= 2);
 
       DivideBy2Command = new Command(() => Number /= 2);
   }
 
   public double Number
   {
       set
       {
           if (number != value)
           {
               number = value;
               PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Number"));
           }
       }
       get
       {
           return number;
       }
   }
 
   public ICommand MultiplyBy2Command { private set; get; }
 
   public ICommand DivideBy2Command { private set; get; }
}

Два свойства ICommand инициализируются в конструкторе класса двумя объектами типа Command. Конструкторы Command включают небольшую функцию (называемую аргументом конструктора execute), которая либо удваивает, либо уменьшает вдвое свойство Number.

Файл BasicButtonCommand.xaml устанавливает свой BindingContext на класс CommandDemoViewModel. Элемент Label и два элемента Button содержат привязки к трем свойствам в CommandDemoViewModel:

      <_local3a_CommandDemoViewModel>
  

При нажатии на два элемента кнопки выполняются команды, и число изменяет свое значение:

Преимущество этого подхода перед обработчиками Clicked заключается в том, что вся логика, связанная с функциональностью этой страницы, находится во модели представления, а не в кодовом файле, что позволяет лучше отделить пользовательский интерфейс от бизнес-логики.

Объекты Command также могут управлять включением и выключением элементов Button. Например, предположим, вы хотите ограничить диапазон значений чисел между 210 и 2-10. Вы можете добавить в конструктор еще одну функцию (с аргументом canExecute), которая возвращает true, если кнопка должна быть включена. Вот модификация конструктора CommandDemoViewModel:

class CommandDemoViewModel : INotifyPropertyChanged
{
   ···
   public CommandDemoViewModel()
   {
       MultiplyBy2Command = new Command(
           execute: () =>
           {
               Number *= 2;
               ((Command)MultiplyBy2Command).ChangeCanExecute();
               ((Command)DivideBy2Command).ChangeCanExecute();
           },
           canExecute: () => Number < Math.Pow(2, 10));
 
       DivideBy2Command = new Command(
           execute: () =>
           {
               Number /= 2;
               ((Command)MultiplyBy2Command).ChangeCanExecute();
               ((Command)DivideBy2Command).ChangeCanExecute();
           },
           canExecute: () => Number > Math.Pow(2, -10));
   }
   ···
}

Вызовы метода ChangeCanExecute метода Command необходимы для того, чтобы метод Command мог вызвать метод canExecute и определить, должна ли кнопка быть отключена или нет. При таком изменении кода, когда число достигает предела, кнопка отключается:

Существует возможность привязки двух или более элементов Button к одному свойству ICommand. Элементы Button можно различать с помощью свойства CommandParameter кнопки (Button). Для этого случая логично использовать общий класс Command. Объект CommandParameter затем передается в качестве аргумента в методы execute и canExecute. Файл MainPage.xaml содержит кнопку для каждой страницы образца:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
            xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
            xmlns:local="clr-namespace:ButtonDemos"
            x:Class="ButtonDemos.MainPage"
            Title="Button Demos">
   <ScrollView>
       <FlexLayout Direction="Column"
                   JustifyContent="SpaceEvenly"
                   AlignItems="Center">
 
           <Button Text="Basic Button Click"
                   Command="{Binding NavigateCommand}"
                   CommandParameter="{x:Type local:BasicButtonClickPage}" />
 
           <Button Text="Code Button Click"
                   Command="{Binding NavigateCommand}"
                   CommandParameter="{x:Type local:CodeButtonClickPage}" />
 
           <Button Text="Basic Button Command"
                   Command="{Binding NavigateCommand}"
                   CommandParameter="{x:Type local:BasicButtonCommandPage}" />
 
           <Button Text="Press and Release Button"
                   Command="{Binding NavigateCommand}"
                   CommandParameter="{x:Type local:PressAndReleaseButtonPage}" />
 
           <Button Text="Button Appearance"
                   Command="{Binding NavigateCommand}"
                   CommandParameter="{x:Type local:ButtonAppearancePage}" />
 
           <Button Text="Toggle Button Demo"
                   Command="{Binding NavigateCommand}"
                   CommandParameter="{x:Type local:ToggleButtonDemoPage}" />
 
           <Button Text="Image Button Demo"
                   Command="{Binding NavigateCommand}"
                   CommandParameter="{x:Type local:ImageButtonDemoPage}" />
 
       </FlexLayout>
   </ScrollView>
</ContentPage>
 

У каждой кнопки свойство Command привязано к свойству NavigateCommand, а параметр CommandParameter установлен на объект Type, соответствующий одному из классов страниц в проекте.

Это свойство NavigateCommand имеет тип ICommand и определено в файле кода:

public partial class MainPage : ContentPage
{
   public MainPage()
   {
       InitializeComponent();
 
       NavigateCommand = new Command(async (Type pageType) =>
       {
           Page page = (Page)Activator.CreateInstance(pageType);
           await Navigation.PushAsync(page);
       });
 
       BindingContext = this;
   }
 
   public ICommand NavigateCommand { private set; get; }
}

Конструктор инициализирует свойство NavigateCommand объектом Command, поскольку Type – это тип объекта CommandParameter, заданный в файле XAML. Это означает, что метод execute имеет аргумент типа Type, который соответствует этому объекту CommandParameter. Функция конкретизирует страницу и затем переходит на нее.

Обратите внимание, что конструктор завершает работу, устанавливая свой BindingContext. Это необходимо для того, чтобы свойства в XAML-файле привязывались к свойству NavigateCommand.

Нажатие и отпускание кнопки

Помимо события Clicked, Button также определяет события Pressed и Released. Событие Pressed происходит, когда палец нажимает на Button или на кнопку мыши, когда указатель расположен над кнопкой. Событие Released происходит, когда палец или кнопка мыши отпускаются. Как правило, одновременно с событием «Clicked» происходит событие «Released», но, если палец или указатель мыши соскользнет с поверхности кнопки перед тем, как ее отпустить, событие «Clicked» может не произойти.

События Pressed и Released используются нечасто, но их можно применять к специальным целям, как показано на странице Press and Release Button. Файл XAML содержит ярлык (Label) и кнопку (Button) с обработчиками, подключенными для событий Pressed и Released:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
            xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
            x:Class="ButtonDemos.PressAndReleaseButtonPage"
            Title="Press and Release Button">
   <StackLayout>
 
       <Label x:Name="label"
              Text="Press and hold the Button below"
              FontSize="Large"
              VerticalOptions="CenterAndExpand"
              HorizontalOptions="Center" />
 
       <Button Text="Press to Rotate Text!"
               VerticalOptions="CenterAndExpand"
               HorizontalOptions="Center"
               Pressed="OnButtonPressed"
               Released="OnButtonReleased" />
 
   </StackLayout>
</ContentPage>

Файл с кодом анимирует Label при возникновении события Pressed, но приостанавливает вращение при возникновении события Released:

public partial class PressAndReleaseButtonPage : ContentPage
{
   bool animationInProgress = false;
   Stopwatch stopwatch = new Stopwatch();
 
   public PressAndReleaseButtonPage ()
   {
       InitializeComponent ();
   }
 
   void OnButtonPressed(object sender, EventArgs args)
   {
       stopwatch.Start();
       animationInProgress = true;
 
       Device.StartTimer(TimeSpan.FromMilliseconds(16), () =>
       {
           label.Rotation = 360 * (stopwatch.Elapsed.TotalSeconds % 1);
 
           return animationInProgress;
       });
   }
 
   void OnButtonReleased(object sender, EventArgs args)
   {
       animationInProgress = false;
       stopwatch.Stop();
   }
}

В результате Label вращается только тогда, когда палец находится в контакте с кнопкой, и останавливается, когда палец отпускается:

Подобное поведение находит применение в играх: палец, удерживаемый на кнопке, может заставить экранный объект двигаться в определенном направлении.

Материалы по теме