单选按钮

单选按钮(也称为选项按钮)允许用户从包含两个或更多互斥但相关的选项的集合中选择一个选项。 单选按钮始终成组使用,每个选项都表示为组中的一个单选按钮。

默认状态下,不会选择 RadioButtons 组中的单选按钮。 也就是说,所有单选按钮都处于清除状态。 但是,用户选择单选按钮后,就不能取消选择该按钮以将组还原到其初始清除状态。

RadioButtons 组的单一行为将其与复选框区分开来,后者支持多选、取消选择或清除。

Example of a RadioButtons group, with one radio button selected

这是正确的控件吗?

使用单选按钮可以让用户从两个或更多互斥的选项中进行选择。

A RadioButtons group, with one radio button selected

当用户需要在做出选择前查看所有选项时,可使用单选按钮。 单选按钮平等地强调所有选项,这意味着有些选项可能会引起超出必要或所需的关注。

除非所有选项都值得平等关注,否则请考虑使用其他控件。 例如,若要在大多数情况下为大多数用户推荐一个最佳选项,请使用组合框将该最佳选项显示为默认选项。

A combo box, displaying a default option

如果只有两个可能的选项,并且这两个选项可以清楚地表示为单个二元选项(例如“开/关”或“是/否”),则可将它们组合为一个复选框切换开关控件。 例如,使用单个复选框“我同意”,而不是两个单选按钮“我同意”和“我不同意”。

请勿使用两个单选按钮来表示单个二元选项:

Two radio buttons presenting a binary choice

改用复选框:

A check box is a good alternative for presenting a binary choice

当用户可以选择多个选项时,请使用复选框

Check boxes support multiselection

当用户的选项为某个值范围(例如,10、20、30...100)时,请使用滑块控件。

A slider control, displaying one value in a range of values

如果有超过八个选项,请使用组合框

A list box, displaying multiple options

如果可用选项基于应用的当前上下文或可以根据其他条件动态变化,请使用列表控件。

建议

  • 请确保一组单选按钮的目的和当前状态十分明确。
  • 将单选按钮的文本标签限制在一行以内。
  • 如果文本标签是动态的,请考虑如何自动调整按钮大小,以及它周围的所有视觉对象将有哪些影响。
  • 请使用默认字体,除非你的品牌指南告诉你使用其他字体。
  • 不要并排放置两个 RadioButtons 组。 当两个 RadioButtons 组并排放置时,用户很难确定哪些按钮属于哪个组。

RadioButtons 概述

RadioButtons 与 RadioButton

有两种方法可以创建单选按钮组:RadioButtons 和 RadioButton。

  • 建议使用 RadioButtons 控件。 此控件可简化布局、处理键盘导航和辅助功能,并支持绑定到数据源。
  • 你可以使用由单独的 RadioButton 控件组成的组

键盘访问和导航行为已在 RadioButtons 控件中进行了优化。 这些改进在辅助功能和键盘功能方面有所帮助,能够让用户更快、更轻松地浏览选项列表。

除这些改进外,RadioButtons 组中各个单选按钮的默认可视布局也通过自动化方向、间距和边距设置进行了优化。 使用更原始的组控件(例如 StackPanelGrid)时可能必须指定这些属性,但在此项优化后无需进行指定。

RadioButtons 控件具有特殊的导航行为,可以帮助键盘用户更加快速轻松地导航列表。

键盘焦点

RadioButtons 控件支持两种状态:

  • 未选择单选按钮
  • 选择了一个单选按钮

下面各节介绍控件在每个状态下的焦点行为。

未选择单选按钮

如果未选择任何单选按钮,则列表中的第一个单选按钮会获得焦点。

注意

不会选择从初始选项卡导航接收选项卡焦点的项。

没有选项卡焦点的列表,未选择任何项

List without tab focus and no selected item

具有初始选项卡焦点的列表,未选择任何项

List with initial tab focus and no selected item

选择了一个单选按钮

当用户进入已选择某个单选按钮的列表时,所选单选按钮会获得焦点。

没有选项卡焦点的列表

List without tab focus and a selected item

具有初始选项卡焦点的列表

List with initial tab focus and a selected item

键盘导航

有关常规键盘导航行为的详细信息,请参阅键盘交互 - 导航

如果 RadioButtons 组中的某个项已具有焦点,则用户可以使用箭头键在组内的各个项之间进行“内部导航”。 可使用向上键和向下键移动到“上一个”或“下一个”逻辑项,就像 XAML 标记中定义的那样。 使用向左键和向右键可进行空间移动。

在单列或单行布局中,键盘导航会导致以下行为:

单列

Example of keyboard navigation in a single-column RadioButtons group

向上键和向下键在项之间移动。
向左箭头和向右箭头键不执行任何操作。

单行

Example of keyboard navigation in a single-row RadioButtons group

使用向左键和向上键可移动到前一项,使用向右键和向下键可移动到下一项。

在多列、多行网格布局中,键盘导航会导致以下行为:

向左键/向右键

Example of horizontal keyboard navigation in a multi-column/row RadioButtons group

使用向左键和向右键可让焦点在同一行中的项之间水平移动。

Example of horizontal keyboard navigation with focus on last item in a column

当焦点位于某列的最后一项并按下向右键或向左键时,焦点会移至下一列或上一列(如果有)中的最后一项。

向上键/向下键

Example of vertical keyboard navigation in a multi-column/row RadioButtons group

使用向上键和向下键可让焦点在同一列中的项之间垂直移动。

Example of vertical keyboard navigation with focus on the last item in a column

当焦点位于某列的最后一项并按下向下键时,焦点会移至下一列(如果有)的第一项。 当焦点位于某列的第一项且按下向上键时,焦点会移至上一列(如果有)的最后一个项

有关详细信息,请参阅键盘交互

换行

RadioButtons 组不会将焦点从第一行或第一列换行到最后一行或最后一列,也不会从最后一行或最后一列换行到第一行或最后一列。 这是因为在用户使用屏幕阅读器时,会失去边界检测和清晰的开始和结束指示,这让有视力障碍的用户难以导航列表。

RadioButtons 控件也不支持枚举,因为该控件用于包含合理数量的项(请参阅这是正确的控件吗?)。

选择跟随焦点

使用键盘在 RadioButtons 组中的各项之间导航时,随着焦点从一个项移到下一个项,将选择新获得焦点的项,并清除之前的焦点项。

键盘导航之前

Example of focus and selection before keyboard navigation

键盘导航之前的焦点和选择。

键盘导航之后

Example of focus and selection after keyboard navigation

键盘导航之后的焦点和选择,其中通过向下键将焦点移到单选按钮 3,选择它,并清除单选按钮 2。

通过使用 Ctrl + 箭头键导航,可以在不更改选择的情况下移动焦点。 移动焦点后,可以使用空格键选择当前具有焦点的项。

如果使用游戏手柄或遥控器在单选按钮之间移动,则会禁用“跟随焦点选择”行为,用户必须按“A”按钮才可选择当前聚焦的单选按钮。

辅助功能行为

下表说明讲述人如何处理 RadioButtons 组和播放的内容。 此行为取决于用户如何设置讲述人详细信息首选项。

操作 讲述人播放内容
焦点移动到所选项 “名称、RadioButton、已选中、第 x 项,共 N 项”
焦点移动到未选中的项
(如果用 Ctrl 键或 Xbox 游戏板进行导航,
这表明所选内容不跟随焦点。)
“名称、RadioButton、未选中、第 x 项,共 N 项”

注意

讲述人为每个项播放的“名称”是 AutomationProperties.Name 附加属性的值(如果该项具有此属性);否则,它是项的 ToString 方法返回的值

x 是当前项在组中的位置。 N 是组中的总项数

UWP 和 WinUI 2

重要

本文中的信息和示例是针对使用 Windows 应用 SDKWinUI 3 的应用优化的,但通常适用于使用 WinUI 2 的 UWP 应用。 有关特定于平台的信息和示例,请查看 UWP API 参考。

本部分包含在 UWP 或 WinUI 2 应用中使用该控件所需的信息。

UWP 应用的 RadioButtons 控件作为 Windows UI 库 2 的一部分被包含在内。 有关详细信息(包括安装说明),请参阅 Windows UI 库。 这些控件的 API 同时存在于 Windows.UI.Xaml.ControlsMicrosoft.UI.Xaml.Controls 命名空间中。

WinUI 2 库应用包括大多数 WinUI 2 控件、特性和功能的交互式示例。 通过 Microsoft Store 获取应用,或在 GitHub 上获取源代码。

可以通过两种方法创建单选按钮组。

  • 从 WinUI 2.3 开始,我们建议使用 RadioButtons 控件。 此控件可简化布局、处理键盘导航和辅助功能,并支持绑定到数据源。
  • 你可以使用由单独的 RadioButton 控件组成的组。 如果你的应用未使用 WinUI 2.3 或更高版本,这是唯一的选择。

建议使用最新的 WinUI 2 来获取所有控件的最新样式和模板。

若要将本文中的代码与 WinUI 2 配合使用,请使用 XAML 中的别名(我们使用 muxc)来表示项目中包含的 Windows UI 库 API。 有关详细信息,请参阅 WinUI 2 入门

xmlns:muxc="using:Microsoft.UI.Xaml.Controls"

<muxc:RadioButtons />

创建 WinUI RadioButtons 组

RadioButtons 控件使用类似于 ItemsControl 的内容模型。 这意味着你可以:

此处,你声明了一个包含三个选项的简单 RadioButtons 控件。 Header 属性设置为向组提供标签,SelectedIndex 属性设置为提供默认选项。

<RadioButtons Header="Background color"
              SelectedIndex="0"
              SelectionChanged="BackgroundColor_SelectionChanged">
    <x:String>Red</x:String>
    <x:String>Green</x:String>
    <x:String>Blue</x:String>
</RadioButtons>

结果类似以下形式:

A group of three radio buttons

要在用户选择某个选项时执行操作,请处理 SelectionChanged 事件。 在此处,更改名为“ExampleBorder”的 Border 元素的背景色 (<Border x:Name="ExampleBorder" Width="100" Height="100"/>)。

private void BackgroundColor_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    if (ExampleBorder != null && sender is RadioButtons rb)
    {
        string colorName = rb.SelectedItem as string;
        switch (colorName)
        {
            case "Red":
                ExampleBorder.Background = new SolidColorBrush(Colors.Red);
                break;
            case "Green":
                ExampleBorder.Background = new SolidColorBrush(Colors.Green);
                break;
            case "Blue":
                ExampleBorder.Background = new SolidColorBrush(Colors.Blue);
                break;
        }
    }
}

提示

还可以从 SelectionChangedEventArgs.AddedItems 属性获取所选项。 在索引 0 处只有一个所选项,因此可以按如下所示来获取它:string colorName = e.AddedItems[0] as string;

选择状态

单选按钮有两个状态:已选择或已清除。 当在 RadioButtons 组中选择某个选项时,可以从 SelectedItem 属性获取其值,从 SelectedIndex 属性获取其在集合中的位置。 如果用户选择了同一组中的另一个单选按钮,则可以清除单选按钮,但如果用户再次选择它,则无法将其清除。 但是,可以通过设置 SelectedItem = nullSelectedIndex = -1,以编程方式清除单选按钮组。 (如果将 SelectedIndex 设置为 Items 集合范围之外的任何值,则不会选择任何内容。)

RadioButtons 内容

前面的示例使用简单的字符串填充了 RadioButtons 控件。 该控件提供了单选按钮,并使用字符串作为每个单选按钮的标签。

但是,你可以用任何对象填充 RadioButtons 控件。 通常,你希望对象提供可以用作文本标签的字符串表示形式。 在某些情况下,图像可能适合代替文本。

此处,SymbolIcon 元素用于填充控件。

<RadioButtons Header="Select an icon option:">
    <SymbolIcon Symbol="Back"/>
    <SymbolIcon Symbol="Attach"/>
    <SymbolIcon Symbol="HangUp"/>
    <SymbolIcon Symbol="FullScreen"/>
</RadioButtons>

A group radio buttons with symbol icons

还可以使用单独的 RadioButton 控件来填充 RadioButtons 项。 我们稍后将讨论这种特殊情况。 请参阅 RadioButtons 组中的 RadioButton 控件

能够使用任何对象的好处是可以将 RadioButtons 控件绑定到数据模型中的自定义类型。 下一节将对此进行说明。

数据绑定

RadioButtons 控件支持将数据绑定到其 ItemsSource 属性。 此示例显示如何将控件绑定到自定义数据源。 此示例的外观和功能与前面的背景色示例相同,但此处,颜色画笔存储在数据模型中,而不是在 SelectionChanged 事件处理程序中创建。

<RadioButtons Header="Background color"
              SelectedIndex="0"
              SelectionChanged="BackgroundColor_SelectionChanged"
              ItemsSource="{x:Bind colorOptionItems}"/>
public sealed partial class MainPage : Page
{
    // Custom data item.
    public class ColorOptionDataModel
    {
        public string Label { get; set; }
        public SolidColorBrush ColorBrush { get; set; }

        public override string ToString()
        {
            return Label;
        }
    }

    List<ColorOptionDataModel> colorOptionItems;

    public MainPage1()
    {
        this.InitializeComponent();

        colorOptionItems = new List<ColorOptionDataModel>();
        colorOptionItems.Add(new ColorOptionDataModel()
            { Label = "Red", ColorBrush = new SolidColorBrush(Colors.Red) });
        colorOptionItems.Add(new ColorOptionDataModel()
            { Label = "Green", ColorBrush = new SolidColorBrush(Colors.Green) });
        colorOptionItems.Add(new ColorOptionDataModel()
            { Label = "Blue", ColorBrush = new SolidColorBrush(Colors.Blue) });
    }

    private void BackgroundColor_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        var option = e.AddedItems[0] as ColorOptionDataModel;
        ExampleBorder.Background = option?.ColorBrush;
    }
}

RadioButtons 组中的 RadioButton 控件

可以使用单独的 RadioButton 控件来填充 RadioButtons 项。 你可能会通过此操作来访问某些属性(如 AutomationProperties.Name);或者,你可能已有 RadioButton 代码,但想要利用 RadioButtons 的布局和导航。

<RadioButtons Header="Background color">
    <RadioButton Content="Red" Tag="red" AutomationProperties.Name="red"/>
    <RadioButton Content="Green" Tag="green" AutomationProperties.Name="green"/>
    <RadioButton Content="Blue" Tag="blue" AutomationProperties.Name="blue"/>
</RadioButtons>

使用 RadioButtons 组中的 RadioButton 控件时,RadioButtons 控件知道如何表示 RadioButton,因此你最终不会做出两个选择。

但是,你应该注意一些行为。 我们建议你在单个控件或 RadioButtons 上处理状态和事件,但不能同时在这两者之上进行处理,以避免冲突。

下表显示了这两个控件上的相关事件和属性。

RadioButton RadioButtons
CheckedUncheckedClick SelectionChanged
IsChecked SelectedItemSelectedIndex

如果在单个 RadioButton 上处理事件(例如 CheckedUnchecked),并且同时处理 RadioButtons.SelectionChanged 事件,则两个事件都将触发。 RadioButton 事件首先发生,RadioButtons.SelectionChanged 事件随后发生,这可能会导致冲突。

IsCheckedSelectedItemSelectedIndex 属性保持同步。 对一个属性的更改将更新其他两个属性。

将忽略 RadioButton.GroupName 属性。 组是由 RadioButtons 控件创建的。

定义多列

默认情况下,RadioButtons 控件在单个列中垂直排列其单选按钮。 可以设置 MaxColumns 属性,使控件在多个列中排列单选按钮。 (执行此操作时,单选按钮按列主序顺序排列,即各个项按照从上到下、然后从左到右的顺序填充。)

<RadioButtons Header="RadioButtons in columns" MaxColumns="3">
    <x:String>Item 1</x:String>
    <x:String>Item 2</x:String>
    <x:String>Item 3</x:String>
    <x:String>Item 4</x:String>
    <x:String>Item 5</x:String>
    <x:String>Item 6</x:String>
</RadioButtons>

Radio buttons in two three-column groups

提示

要将项排列在单个水平行中,请将 MaxColumns 设置为组中的项数。

创建自己的 RadioButton 组

重要

建议使用 RadioButtons 控件对 RadioButton 元素进行分组。

单选按钮以组形式工作。 可以通过以下两种方法之一对各 RadioButton 控件进行分组:

  • 将它们放在同一个父容器内。
  • 将每个单选按钮上的 GroupName 属性设置为相同的值。

在此示例中,第一个单选按钮组依据位于相同的堆栈面板中来进行隐式分组。 第二组分为两个堆栈面板,因此使用 GroupName 将它们显式分组为单个组。

<StackPanel>
    <StackPanel>
        <TextBlock Text="Background" Style="{ThemeResource BaseTextBlockStyle}"/>
        <!-- Group 1 - implicit grouping -->
        <StackPanel Orientation="Horizontal">
            <RadioButton Content="Green" Tag="green" Checked="BGRadioButton_Checked"/>
            <RadioButton Content="Yellow" Tag="yellow" Checked="BGRadioButton_Checked"/>
            <RadioButton Content="White" Tag="white" Checked="BGRadioButton_Checked"
                         IsChecked="True"/>
        </StackPanel>
    </StackPanel>

    <StackPanel>
        <TextBlock Text="BorderBrush" Style="{ThemeResource BaseTextBlockStyle}"/>
        <!-- Group 2 - grouped by GroupName -->
        <StackPanel Orientation="Horizontal">
            <StackPanel>
                <RadioButton Content="Green" Tag="green" GroupName="BorderBrush"
                             Checked="BorderRadioButton_Checked"/>
                <RadioButton Content="Yellow" Tag="yellow" GroupName="BorderBrush"
                             Checked="BorderRadioButton_Checked" IsChecked="True"/>
                <RadioButton Content="White" Tag="white"  GroupName="BorderBrush"
                             Checked="BorderRadioButton_Checked"/>
            </StackPanel>
        </StackPanel>
    </StackPanel>
    <Border x:Name="ExampleBorder"
            BorderBrush="#FFFFD700" Background="#FFFFFFFF"
            BorderThickness="10" Height="50" Margin="0,10"/>
</StackPanel>
private void BGRadioButton_Checked(object sender, RoutedEventArgs e)
{
    RadioButton rb = sender as RadioButton;

    if (rb != null && ExampleBorder != null)
    {
        string colorName = rb.Tag.ToString();
        switch (colorName)
        {
            case "yellow":
                ExampleBorder.Background = new SolidColorBrush(Colors.Yellow);
                break;
            case "green":
                ExampleBorder.Background = new SolidColorBrush(Colors.Green);
                break;
            case "white":
                ExampleBorder.Background = new SolidColorBrush(Colors.White);
                break;
        }
    }
}

private void BorderRadioButton_Checked(object sender, RoutedEventArgs e)
{
    RadioButton rb = sender as RadioButton;

    if (rb != null && ExampleBorder != null)
    {
        string colorName = rb.Tag.ToString();
        switch (colorName)
        {
            case "yellow":
                ExampleBorder.BorderBrush = new SolidColorBrush(Colors.Gold);
                break;
            case "green":
                ExampleBorder.BorderBrush = new SolidColorBrush(Colors.DarkGreen);
                break;
            case "white":
                ExampleBorder.BorderBrush = new SolidColorBrush(Colors.White);
                break;
        }
    }
}

这两组 RadioButton 控件如下所示:

Radio buttons in two groups

单选按钮状态

单选按钮有两个状态:已选择或已清除。 选择单选按钮时,其 IsChecked 属性为 true。 清除单选按钮时,其 IsChecked 属性为 false。 如果用户选择了同一组中的另一个单选按钮,则可以清除单选按钮,但如果用户再次选择它,则无法将其清除。 但是,你可以通过将单选按钮的 IsChecked 属性设置为 false,以编程方式清除它。

要考虑的视觉对象

RadioButton 控件的默认间距与 RadioButtons 组提供的间距不同。 要将 RadioButtons 间距应用于各 RadioButton 控件,请使用 Margin0,0,7,3,如下所示。

<StackPanel>
    <StackPanel.Resources>
        <Style TargetType="RadioButton">
            <Setter Property="Margin" Value="0,0,7,3"/>
        </Style>
    </StackPanel.Resources>
    <TextBlock Text="Background"/>
    <RadioButton Content="Item 1"/>
    <RadioButton Content="Item 2"/>
    <RadioButton Content="Item 3"/>
</StackPanel>

下图显示了组中单选按钮的首选间距。

Image showing a set of radio buttons, arranged vertically

Image showing spacing guidelines for radio buttons

注意

如果使用 WinUI RadioButtons 控件,则间距、边距和方向都已经过优化。

获取示例代码

  • WinUI 库:此示例以交互格式显示所有 XAML 控件。