如何对CheckBoxes进行分组(可以对RadioButtons进行分组),以便在选中其中一个时取消选中其他选项?
修改
使用CheckBoxes的动机是,默认情况下,一旦选中,它们允许用户取消选择它们,而RadioButtons则不允许(没有添加额外的逻辑)。此外,这种类型的UI元素分组更自然地映射到可以为空的bool值,其中有三个有效选项:selected,not selected,null(或根本没有选择)。这是某些场景的有效用例。 RadioButton
或CheckBox
都不能完全符合这个范例。
从用户的角度来看,完成此操作的控件实际上是任意的,因为一个只是一个填充的圆,而另一个是一个填充的正方形,你可以很容易地渲染为另一个,所以我不认为混淆任何人是一个很大的问题。我想说一个用户不太可能被一个预期有一个圆圈的方格所困扰,而且被迫在两个RadioButton
选项之间做出决定更加沮丧,他们不希望在两个选项之间做出选择。 。实际上,可以创建支持分组的CheckBoxes或支持取消选择的RadioButtons来实现这一点,这个问题是关于前者的。
答案 0 :(得分:0)
我在CodeBehind中不得不一直这样做很沮丧,因为我推出了自己继承自CheckBox
的类,它使用RadioButton
提供的相同API添加了功能。它缺少一些东西,但这是为了后人的缘故。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace GroupedCheckBoxTest
{
public class GroupedCheckBox : CheckBox
{
/// <summary>
/// All the members of the current group
/// </summary>
private IEnumerable<GroupedCheckBox> currentGroup;
/// <summary>
/// Gets or sets the name that specifies which GroupedCheckBox controls are mutually exclusive.
/// </summary>
public string GroupName { get; set; }
/// <summary>
/// Indicates when the checked state of the GroupedCheckBox changes
/// </summary>
public event EventHandler CheckChanged;
public GroupedCheckBox()
{
// Add only the current checkbox to the group
// (for now, but potentially always)
currentGroup = this.SingleItemAsEnumerable();
// Attach empty delegate so we don't have to keep checking for null
this.CheckChanged += delegate { };
// Aggregate all checked changed events
this.Checked += GroupedCheckBox_Checked;
// Associate all GroupedCheckBoxs once they are loaded
this.Loaded += GroupedCheckBox_Loaded;
}
// False when any one of the group checked
void GroupedCheckBox_Checked(object sender, RoutedEventArgs e)
{
// Uncheck everyone else in the group
foreach (GroupedCheckBox otherCB in currentGroup.Except(this.SingleItemAsEnumerable()))
{
// Uncheck the other checkbox
otherCB.IsChecked = false;
}
this.CheckChanged(this, e);
}
void GroupedCheckBox_Loaded(object sender, RoutedEventArgs e)
{
// If this GroupedCheckBox isn't even part of a group
if (string.IsNullOrEmpty(this.GroupName))
{
// We don't need to do anything special
return;
}
// The highest parent node we can get to
DependencyObject parentNode = this;
// Walk the control tree until we get to the top
while (VisualTreeHelper.GetParent(parentNode) != null)
{
// Get the parent of the current node
parentNode = VisualTreeHelper.GetParent(parentNode);
}
// Get all GroupedCheckBox's with the same GroupName as this one
currentGroup = parentNode.GetControlsOfType<GroupedCheckBox>().Where(cb => this.GroupName == cb.GroupName);
}
}
internal static class GroupedCheckBoxHelperExtensions
{
public static IEnumerable<T> GetControlsOfType<T>(this DependencyObject depObj) where T : DependencyObject
{
if (depObj != null)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
if (child != null && child is T)
{
yield return (T)child;
}
foreach (T childOfChild in GetControlsOfType<T>(child))
{
yield return childOfChild;
}
}
}
}
// usage: someObject.SingleItemAsEnumerable();
public static IEnumerable<T> SingleItemAsEnumerable<T>(this T item)
{
yield return item;
}
}
}
只需将其复制到GroupedCheckBox.cs并添加适当的xaml引用并按如下方式使用:
<UserControl x:Class="UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:controls="clr-namespace:YourProject.Controls"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<controls:GroupedCheckBox Grid.Row="0" Grid.Column="0" IsChecked="{Binding Path=Stop}" GroupName="1" />
<controls:GroupedCheckBox Grid.Row="0" Grid.Column="1" IsChecked="{Binding Path=Go}" GroupName="1" />
<controls:GroupedCheckBox Grid.Row="1" Grid.Column="0" IsChecked="{Binding Path=Yes}" GroupName="2" />
<controls:GroupedCheckBox Grid.Row="1" Grid.Column="1" IsChecked="{Binding Path=No}" GroupName="2" />
</Grid>
</UserControl>
答案 1 :(得分:0)
在previouse answer中,您可以使用伪装成CheckBox的RadioButton。或者您在ViewModel的代码中执行逻辑。
但是有一个原因,没有CheckBox分组:RadioButton的语义是只能选择其中一个。 CheckBox向用户发出信号,表示这些框彼此独立。
所以我建议坚持使用RadioButtons,除非有任何其他原因。