Button в .Net MAUI

Автор:

learn.microsoft.com

В пользовательском интерфейсе приложения NET (.NET MAUI) элемент Button отвечает за отображение текста и реакцию на нажатие, которое указывает приложению на то, что нужно начать выполнение задачи. Обычно Button отображает короткую текстовую строку, указывающую на команду, но она может также отображать растровое изображение или комбинацию текста и изображения. При нажатии на кнопку пальцем или щелчке мышкой она инициирует выполнение этой команды.

Button в .NET MAUI определяет следующие свойства:

  • BorderColor с типом Color описывает цвет границы кнопки.
  • BorderWidth с типом double определяет ширину границы кнопки.
  • CharacterSpacing с типом double определяет расстояние между символами текста кнопки.
  • Command с типом ICommand определяет команду, выполняемую при нажатии на кнопку.
  • CommandParameter с типом object является параметром, передаваемым в Command.
  • ContentLayout с типом ButtonContentLayout определяет объект, управляющий положением изображения кнопки и расстоянием между изображением и текстом кнопки.
  • CornerRadius с типом int описывает радиус угла границы кнопки.
  • FontAttributes с типом FontAttributes определяет стиль текста.
  • FontAutoScalingEnabled с типом bool определяет, будет ли текст кнопки отражать настройки масштабирования, заданные в операционной системе. По умолчанию значение этого свойства равно true.
  • FontFamily с типом string определяет семейство шрифтов.
  • FontSize с типом double определяет размер шрифта.
  • ImageSource с типом ImageSource задает растровое изображение для отображения в качестве содержимого кнопки.
  • LineBreakMode с типом LineBreakMode определяет, как должен обрабатываться текст, если он не помещается в одну строку.
  • Padding, с типом Thickness определяет толщину подложки кнопки.
  • Text с типом string определяет текст, отображаемый в качестве содержимого кнопки.
  • TextColor с типом Color описывает цвет текста кнопки.
  • TextTransform с типом TextTransform определяет обводку текста кнопки.

Эти свойства поддерживаются объектами BindableProperty, что означает, что они могут быть объектами привязки к данным и стилизованы.

Примечание. Хотя Button определяет свойство ImageSource, позволяющее выводить на кнопку изображение, это свойство предназначено для использования при отображении небольшой пиктограммы рядом с текстом кнопки.

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

Важно. Для того чтобы кнопка реагировала на нажатия, ее свойство IsEnabled должно иметь значение true.

Создание Button в .NET MAUI

Чтобы создать кнопку, создайте объект Button и обработайте его событие Clicked.

В следующем примере XAML показано, как создать кнопку:

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
            xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
            x:Class="ButtonDemos.BasicButtonClickPage"
            Title="Basic Button Click">
   <StackLayout>
       <Button Text="Click to Rotate Text!"
               VerticalOptions="Center"
               HorizontalOptions="Center"
               Clicked="OnButtonClicked" />
       <Label x:Name="label"
              Text="Click the Button above"
              FontSize="18"
              VerticalOptions="Center"
              HorizontalOptions="Center" />
   </StackLayout>
</ContentPage>

Свойство Text задает текст, который отображается в кнопке. Событие Clicked устанавливается в обработчик события OnButtonClicked. Этот обработчик находится в файле кода программной части:

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 миллисекунд:

Эквивалентный код на языке C# для создания кнопки имеет следующий вид:

Button button = new Button
{
   Text = "Click to Rotate Text!",
   VerticalOptions = LayoutOptions.Center,
   HorizontalOptions = LayoutOptions.Center
};
button.Clicked += async (sender, args) => await label.RelRotateTo(360, 1000);

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

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

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

Такой подход особенно удобен в связи с привязкой данных и, в частности, при реализации паттерна Model-View-ViewModel (MVVM). В MVVM-приложении модель представления определяет свойства типа ICommand, которые затем связываются с объектами Button с помощью привязки к данным. В .NET MAUI также определены классы Command и Command, реализующие интерфейс ICommand и помогающие вью-модели определять свойства типа ICommand.

В следующем примере показан очень простой класс viewmodel, определяющий свойство типа double под названием Number и два свойства типа ICommand под названиями MultiplyBy2Command и DivideBy2Command:

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

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

Следующий пример XAML использует класс CommandDemoViewModel:

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
            xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
            xmlns:local="clr-namespace:ButtonDemos"
            x:Class="ButtonDemos.BasicButtonCommandPage"
            Title="Basic Button Command">
   <ContentPage.BindingContext>
       <local:CommandDemoViewModel />
   </ContentPage.BindingContext>
 
   <StackLayout>
       <Label Text="{Binding Number, StringFormat='Value is now {0}'}"
              FontSize="18"
              VerticalOptions="Center"
              HorizontalOptions="Center" />
       <Button Text="Multiply by 2"
               VerticalOptions="Center"
               HorizontalOptions="Center"
               Command="{Binding MultiplyBy2Command}" />
       <Button Text="Divide by 2"
               VerticalOptions="Center"
               HorizontalOptions="Center"
               Command="{Binding DivideBy2Command}" />
   </StackLayout>
</ContentPage>

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

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

public 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.

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

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

В следующем примере XAML показаны Label и Button с подключенными обработчиками для событий Pressed и Released:

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
            xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
            x:Class="ButtonDemos.PressAndReleaseButtonPage"
            Title="Press and Release Button">
   <StackLayout>
       <Button Text="Press to Rotate Text!"
               VerticalOptions="Center"
               HorizontalOptions="Center"
               Pressed="OnButtonPressed"
               Released="OnButtonReleased" />
       <Label x:Name="label"
              Text="Press and hold the Button above"
              FontSize="18"
              VerticalOptions="Center"
              HorizontalOptions="Center" />
   </StackLayout>
</ContentPage>

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

public partial class PressAndReleaseButtonPage : ContentPage
{
   IDispatcherTimer timer;
   Stopwatch stopwatch = new Stopwatch();
 
   public PressAndReleaseButtonPage()
   {
       InitializeComponent();
 
       timer = Dispatcher.CreateTimer();
       timer.Interval = TimeSpan.FromMilliseconds(16);
       timer.Tick += (s, e) =>
       {
           label.Rotation = 360 * (stopwatch.Elapsed.TotalSeconds % 1);
       };
   }
 
   void OnButtonPressed(object sender, EventArgs args)
   {
       stopwatch.Start();
       timer.Start();
   }
 
   void OnButtonReleased(object sender, EventArgs args)
   {
       stopwatch.Stop();
       timer.Stop();
   }
}

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

Визуальные состояния кнопки

Кнопка имеет визуальное состояние Pressed, которое может использоваться для инициирования визуального изменения кнопки при нажатии, если оно включено.

В следующем примере XAML показано, как определить визуальное состояние для состояния Pressed:

<Button Text="Click me!"
       ...>
   <VisualStateManager.VisualStateGroups>
       <VisualStateGroup x:Name="CommonStates">
           <VisualState x:Name="Normal">
               <VisualState.Setters>
                   <Setter Property="Scale"
                           Value="1" />
               </VisualState.Setters>
           </VisualState>
           <VisualState x:Name="Pressed">
               <VisualState.Setters>
                   <Setter Property="Scale"
                           Value="0.8" />
               </VisualState.Setters>
           </VisualState>
       </VisualStateGroup>
   </VisualStateManager.VisualStateGroups>
</Button>

В данном примере состояние Pressed VisualState определяет, что при нажатии кнопки ее свойство Scale будет изменено со значения по умолчанию 1 на 0,8. Визуальное состояние Normal определяет, что, когда кнопка находится в нормальном состоянии, ее свойство Scale будет установлено в 1. Таким образом, общий эффект заключается в том, что при нажатии кнопки ее масштаб немного уменьшается, а при отпускании - возвращается к стандартному размеру.

Использование растровых изображений в кнопках

Класс Button в .NET MAUI определяет свойство ImageSource, которое позволяет отображать на кнопке небольшое растровое изображение, как отдельно, так и в сочетании с текстом. Можно также указать, как будут располагаться текст и изображение. Свойство ImageSource имеет тип ImageSource, что означает, что растровые изображения могут быть загружены из файла, встроенного ресурса, URI или потока.

Растровые изображения не масштабируются для установки на кнопку. Оптимальный размер обычно составляет от 32 до 64 единиц измерения, в зависимости от того, насколько большим вы хотите видеть растровое изображение.

Задать расположение свойств Text и ImageSource на кнопке можно с помощью свойства ContentLayout кнопки Button. Это свойство имеет тип ButtonContentLayout, а его конструктор имеет два аргумента:

  • Элемент перечисления ImagePosition: Left, Top, Right или Bottom, указывающий, как растровое изображение располагается относительно текста.
  • Значение double интервала между растровым изображением и текстом.

В XAML можно создать кнопку и установить свойство ContentLayout, указав только член перечисления, или интервал, или оба в любом порядке через запятую:

<Button Text="Button text"
       ImageSource="button.png"
       ContentLayout="Right, 20" />

Эквивалентный код на языке C# имеет вид:

Button button = new Button
{
   Text = "Button text",
   ImageSource = new FileImageSource
   {
       File = "button.png"
   },
   ContentLayout = new Button.ButtonContentLayout(Button.ButtonContentLayout.ImagePosition.Right, 20)
};

Отключение Button в .NET MAUI

Иногда приложение переходит в состояние, когда нажатие кнопки не является допустимой операцией. В таких случаях кнопку можно отключить, установив для ее свойства IsEnabled значение false.

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