Xamarin.Forms 步进器

使用步进器从一系列值中选择一个数值。

Xamarin.FormsStepper 由两个分别标有减号和加号的按钮组成。 用户可以操作这些按钮,以增量方式从一系列值中选择一个 double 值。

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

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

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

警告

在内部,Stepper 确保 Minimum 小于 Maximum。 如果曾经设置过 MinimumMaximum,使其 Minimum 不小于 Maximum,则会引发异常。 若要详细了解如何设置 MinimumMaximum 属性,请参阅预防措施部分。

Stepper 强制 Value 属性,使其介于 MinimumMaximum(包含端点值)之间。 如果 Minimum 属性设置为大于 Value 属性的值,Stepper 会将 Value 属性设置为 Minimum。 同样,如果 Maximum 设置为小于 Value 的值,Stepper 会将 Value 属性设置为 Maximum

Stepper 定义一个 ValueChanged 事件;当 Value 因用户操作 Stepper 或应用程序直接设置 Value 属性而发生变化时,会触发此事件。 如前一段所述,当 Value 属性被强制执行时,也会触发 ValueChanged 事件。

ValueChanged 事件随附的 ValueChangedEventArgs 对象有两个类型为 double 的属性:OldValueNewValue。 在事件被触发时,NewValue 的值与 Stepper 对象的 Value 属性相同。

基本步进器代码和标记

该示例包含三个页面,这些页面在功能上相同,但以不同的方式实现。 第一个页面仅使用 C# 代码,第二个页面在代码中将 XAML 和事件处理程序结合使用,第三个页面能够在 XAML 文件中使用数据绑定来避免事件处理程序。

在代码中创建步进器

示例中的“基本步进器代码”页面演示如何在代码中创建 Stepper 和两个 Label 对象:

public class BasicStepperCodePage : ContentPage
{
    public BasicStepperCodePage()
    {
        Label rotationLabel = new Label
        {
            Text = "ROTATING TEXT",
            FontSize = Device.GetNamedSize(NamedSize.Large, typeof(Label)),
            HorizontalOptions = LayoutOptions.Center,
            VerticalOptions = LayoutOptions.CenterAndExpand
        };

        Label displayLabel = new Label
        {
            Text = "(uninitialized)",
            HorizontalOptions = LayoutOptions.Center,
            VerticalOptions = LayoutOptions.CenterAndExpand
        };

        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);
        };

        Title = "Basic Stepper Code";
        Content = new StackLayout
        {
            Margin = new Thickness(20),
            Children = { rotationLabel, stepper, displayLabel }
        };
    }
}

会初始化 Stepper,使其 Maximum 属性为 360,Increment 属性为 30。 根据 Increment 属性的值,对 Stepper 进行操作会以增量方式在 MinimumMaximum 之间更改所选值。 StepperValueChanged 处理程序使用 stepper 对象的 Value 属性来设置第一个 LabelRotation 属性,将 string.Format 方法与事件参数的 NewValue 属性结合使用来设置第二个 LabelText 属性。 这两种获取 Stepper 的当前值的方法是可互换的。

以下屏幕截图显示了“基本步进器代码”页面:

基本步进器代码

第二个 Label 显示“(未初始化)”文本,直到 Stepper 被操作,这会导致触发第一个 ValueChanged 事件。

在 XAML 中创建步进器

“基本步进器 XAML”页面在功能上与“基本滑步进器代码”相同,但主要在 XAML 中实现:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             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="Large"
               HorizontalOptions="Center"
               VerticalOptions="CenterAndExpand" />
        <Stepper Maximum="360"
                 Increment="30"
                 HorizontalOptions="Center"
                 ValueChanged="OnStepperValueChanged" />
        <Label x:Name="_displayLabel"
               Text="(uninitialized)"
               HorizontalOptions="Center"
               VerticalOptions="CenterAndExpand" />        
    </StackLayout>
</ContentPage>

代码隐藏文件包含 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);
    }
}

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

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

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

double value = stepper.Value;

数据绑定步进器

“基本步进器绑定”页面演示了如何编写一个几乎等效的应用程序,该程序通过使用数据绑定来消除 Value 事件处理程序:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             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 Source={x:Reference _stepper}, Path=Value}"
               FontSize="Large"
               HorizontalOptions="Center"
               VerticalOptions="CenterAndExpand" />
        <Stepper x:Name="_stepper"
                 Maximum="360"
                 Increment="30"
                 HorizontalOptions="Center" />
        <Label Text="{Binding Source={x:Reference _stepper}, Path=Value, StringFormat='The Stepper value is {0:F0}'}"
               HorizontalOptions="Center"
               VerticalOptions="CenterAndExpand" />
    </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 属性被强制设置为 100 的 Maximum 值。

下面是上述代码片段:

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

Minimum 设置为 180 时,Value 也设置为 180。

如果在 Value 属性强制转换为默认值 0 以外的其他值时附加了 ValueChanged 事件处理程序,则会触发 ValueChanged 事件。 下面是 XAML 代码片段:

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

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