步进器

.NET Multi-platform App UI (.NET MAUI) Stepper 允许从一系列值中选择数值。 它由两个带有减号和加号的按钮组成。 用户可以操作这些按钮,以增量方式从一系列值中选择一个 double 值。

Stepper 定义了类型为 double 的四个属性。

  • Increment 是所选值的变化量,默认值为 1。
  • Minimum 是最小范围,默认值为 0。
  • Maximum 是最大范围,默认值为 100。
  • Value 是步进器的值,其范围介于 MinimumMaximum 之间,默认值为 0。

所有这些属性都由 BindableProperty 对象支持。 Value 属性具有 BindingMode.TwoWay 的默认绑定模式,这意味着它适合用作使用模型-视图-视图模型 (MVVM) 模式的应用程序中的绑定源。

Stepper 会强制转换 Value 属性,使其介于 MinimumMaximum 之间(含)。 如果 Minimum 属性被设置为大于 Value 属性的值,则 Stepper 会将 Value 属性设置为 Minimum。 同样,如果 Maximum 被设置为小于 Value 的值,则 Stepper 会将 Value 属性设置为 Maximum。 在内部,Stepper 可确保 Minimum 小于 Maximum。 如果曾经设置过 MinimumMaximum,则 Minimum 不小于 Maximum,将会引发异常。 有关设置 MinimumMaximum 属性的详细信息,请参阅预防措施

Stepper 定义在 Value 发生更改时引发的 ValueChanged 事件,该事件是通过 Stepper 的用户操作或在应用程序直接设置 Value 属性时引发的。 如前所述,当强制转换 Value 属性时,也会引发 ValueChanged 事件。 随附 ValueChanged 事件的 ValueChangedEventArgs 对象有类型为 doubleOldValueNewValue。 在引发事件时,NewValue 的值与 Stepper 对象的 Value 属性相同。

创建 Stepper

下面的示例展示了如何用 Label 对象创建 Stepper

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="StepperDemo.BasicStepperXAMLPage"
             Title="Basic Stepper XAML">
    <StackLayout Margin="20">
        <Label x:Name="_rotatingLabel"
               Text="ROTATING TEXT"
               FontSize="18"
               HorizontalOptions="Center"
               VerticalOptions="Center" />
        <Stepper Maximum="360"
                 Increment="30"
                 HorizontalOptions="Center"
                 ValueChanged="OnStepperValueChanged" />
        <Label x:Name="_displayLabel"
               Text="(uninitialized)"
               HorizontalOptions="Center"
               VerticalOptions="Center" />        
    </StackLayout>
</ContentPage>

在此示例中,对 Stepper 进行了初始化,使其具有一个值为 360 的 Maximum 属性,和一个值为 30 的 Increment 属性。 根据 Increment 属性的值,对 Stepper 进行操作会以增量方式在 MinimumMaximum 之间更改所选值。 第二个 Label 显示文本“(uninitialized)”,直到操作了 Stepper,这将导致引发第一个 ValueChanged 事件。

代码隐藏文件包含 ValueChanged 事件的处理程序:

public partial class BasicStepperXAMLPage : ContentPage
{
    public BasicStepperXAMLPage()
    {
        InitializeComponent();
    }

    void OnStepperValueChanged(object sender, ValueChangedEventArgs e)
    {
        double value = e.NewValue;
        _rotatingLabel.Rotation = value;
        _displayLabel.Text = string.Format("The Stepper value is {0}", value);
    }
}

StepperValueChanged 处理程序使用 stepper 对象的 Value 属性来设置第一个 LabelRotation 属性,并将 string.Format 方法与事件参数的 NewValue 属性一起使用来设置第二个 LabelText 属性:

.NET MAUI Stepper screenshot.

事件处理程序还可能通过 sender 参数获取触发事件的 StepperValue 属性包含当前值:

double value = ((Stepper)sender).Value;

如果 Stepper 对象在 XAML 文件中指定了带 x:Name 属性(例如“stepper”)的名称,则事件处理程序可以直接引用该对象:

double value = stepper.Value;

用于创建 Stepper 的等效 C# 代码为:

Stepper stepper = new Stepper
{
    Maximum = 360,
    Increment = 30,
    HorizontalOptions = LayoutOptions.Center
};
stepper.ValueChanged += (sender, e) =>
{
    rotationLabel.Rotation = stepper.Value;
    displayLabel.Text = string.Format("The Stepper value is {0}", e.NewValue);
};

数据绑定 Stepper

可以使用数据绑定来响应 Stepper 值的更改,从而消除 ValueChanged 事件处理程序:

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="StepperDemo.BasicStepperBindingsPage"
             Title="Basic Stepper Bindings">
    <StackLayout Margin="20">
        <Label Text="ROTATING TEXT"
               Rotation="{Binding x:DataType='Stepper', Source={x:Reference _stepper}, Path=Value}"
               FontSize="18"
               HorizontalOptions="Center"
               VerticalOptions="Center" />
        <Stepper x:Name="_stepper"
                 Maximum="360"
                 Increment="30"
                 HorizontalOptions="Center" />
        <Label Text="{Binding x:DataType='Stepper', Source={x:Reference _stepper}, Path=Value, StringFormat='The Stepper value is {0:F0}'}"
               HorizontalOptions="Center"
               VerticalOptions="Center" />
    </StackLayout>
</ContentPage>

在此示例中,第一个 LabelRotation 属性绑定到 StepperValue 属性,第二个 LabelText 属性具有 StringFormat 规范。 第一页出现时,第二个 Label 将显示具有该值的文本字符串。 若要显示没有数据绑定的文本,需要通过从类构造函数调用事件处理程序来专门初始化 LabelText 属性或模拟 ValueChanged 事件的触发。

预防措施

Minimum 属性的值必须始终小于 Maximum 属性的值。 下面的代码示例会导致 Stepper 引发异常:

// Throws an exception!
Stepper stepper = new Stepper
{
    Minimum = 180,
    Maximum = 360
};

C# 编译器生成了按顺序设置这两个属性的代码,当 Minimum 属性被设置为 180 时,它大于默认 Maximum 值 100。 可以通过先设置 Maximum 属性来避免此情况下出现的异常:

Stepper stepper = new Stepper
{
    Maximum = 360,
    Minimum = 180
};

在此示例中,将 Maximum 设置为 360 不会造成问题,因为它大于默认 Minimum 值 0。 设置 Minimum 时,该值小于 Maximum 值 360。

XAML 中存在相同的问题。 设置属性是为了确保 Maximum 始终大于 Minimum

<Stepper Maximum="360"
         Minimum="180" ... />

可以将 MinimumMaximum 值设置为负数,但只能按照 Minimum 始终小于 Maximum 的顺序:

<Stepper Minimum="-360"
         Maximum="-180" ... />

Value 属性始终大于或等于 Minimum 值且小于或等于 Maximum。 如果将 Value 设置为该范围之外的值,则会强制使此值位于该范围内,但不会引发异常。 例如,此代码不会引发异常:

Stepper stepper = new Stepper
{
    Value = 180
};

而是会将 Value 属性的 Maximum 值强制转换为 100。

前面的示例将 Maximum 设置为 360,将 Minimum 设置为 180:

Stepper stepper = new Stepper
{
    Maximum = 360,
    Minimum = 180
};

如果 Minimum 被设置为 180,则 Value 也会被设置为 180。

如果在将 Value 属性强制转换为其默认值 0 以外的其他值时附加了 ValueChanged 事件处理程序,则会引发 ValueChanged 事件:

<Stepper ValueChanged="OnStepperValueChanged"
         Maximum="360"
         Minimum="180" />

如果 Minimum 被设置为 180,则 Value 也会被设置为 180,并引发 ValueChanged 事件。 在构造页面的其余部分之前,可能会发生这种情况,处理程序可能会尝试引用尚未创建的页面上的其他元素。 你需要向 ValueChanged 处理程序添加一些代码,以检查页面上其他元素的 null 值。 或者,可以在初始化 Stepper 值后设置 ValueChanged 事件处理程序。