我有一个 ListBox 控件,我想将其更改为具有切换选择。即单击一个项目选择它,再次单击它取消选择它。此外,单击列表中的另一个项目应该执行取消选择前一个项目并选择新项目的默认操作。
实现这一目标的最佳方法是什么?
我有一个 ListBox 控件,我想将其更改为具有切换选择。即单击一个项目选择它,再次单击它取消选择它。此外,单击列表中的另一个项目应该执行取消选择前一个项目并选择新项目的默认操作。
实现这一目标的最佳方法是什么?
你想要的是一个 RadioButtonList。您可以通过创建 ItemDataTemplate 并将 RadioButton 放入其中来创建一个。然后您可以修改 RadioButton 的模板,使其看起来像一个按钮。
我会保留标准的 ListBox,因为这样你会保留你想要的默认行为,然后为了取消选择一个项目,你可以在鼠标按下事件中处理它吗?即使用这样的东西:
Point newPoint = e.GetPosition(backgroundImage);
HitTestResult result = VisualTreeHelper.HitTest(this, newPoint);
if (result.VisualHit is ListBoxItem)
我不确定这是否是最好的方法,我要做的唯一其他方法是从标准 Windows 控件派生我自己的 ListBox 并添加一个依赖属性以允许我处理样式中的取消选择。如果您想采用这种方法,我也不应该删除您想要保留的默认行为,我使用这种方法创建了一个在文本旁边显示图像的 ListBox。
如果你需要任何关于从哪里开始的指示,那就给我喊一声。
我通过稍微创建两个自定义类 ToggleListBox 和 ToggleListBoxItem 自己设法实现了这一点。有几个方便的覆盖(ToggleListBox 中最重要的是创建 ListBoxItem 的那个)。
从那里很容易处理两次单击同一项目,或按空格/输入键。这两个类都有 40 行代码,并且工作正常。
以防万一有人感兴趣,我找到了一个不同的解决方案,这对我来说非常有效。我设置ListBox.SelectionMode
为Multiple
并处理了 MouseDown 事件:
EventManager.RegisterClassHandler(typeof(ListBoxItem),
ListBoxItem.MouseLeftButtonDownEvent,
new RoutedEventHandler(this.HandleListBox_MouseDown));
...
void HandleListBox_MouseDown(object sender, RoutedEventArgs e)
{
var listBoxItem = (ListBoxItem)sender;
if (ShouldDeselectOtherListItems(listBoxItem))
{
listBox.SelectedIndex = -1;
}
}
bool ShouldDeselectOtherListItems(ListBoxItem listBoxItem)
{
return !listBoxItem.IsSelected
&& listBox.SelectedItems.Count > 0;
}
其中 listBox 是父 ListBox 控件。它确保在处理系统 MouseDown 事件处理程序之前取消选择所有列表框项,从而允许切换相同列表框项的选择状态。
我开始寻找一种“内置”的方式来做到这一点。当我看到没有,并注意到这里有一些我不太喜欢的答案时,我想我会发布我认为最好的方法。
这个答案与Bijington的答案最相似我只是在 ListBox 中添加了一个 PreviewMouseDown 处理程序
<ListBox ... PreviewMouseLeftButtonDown="ListBox_OnPreviewMouseLeftButtonDown"... />
然后在代码中
private void ListBox_OnPreviewMouseLeftButtonDown (object sender, MouseButtonEventArgs e)
{
// I have a special extension for GetParent, numerous examples on the internet of how you would do that
var lbi = ((DependencyObject) e.OriginalSource).GetParent<ListBoxItem>();
if (lbi != null && lbi.IsSelected)
{
lbi.IsSelected = false;
e.Handled = true;
}
}
然后我认为将它变成附加属性会很好,所以这里有一些伪代码来解释如何做到这一点......
public static class ListBoxEx
{
private static DependencyProperty ToggleSelectionProperty = DependencyProperty.RegisterAttached(..., HandleToggleSelectionChanged);
private static bool GetToggleSelection (DependencyObject obj) => (bool)obj.GetValue(ToggleSelectionProperty);
private static void SetToggleSelection (DependencyObject obj, bool shouldToggle) => obj.SetValue(ToggleSelectionProperty, shouldToggle);
private static void HandleToggleSelectionChanged (DependencyObject obj)
{
if (obj is ListBox listBoxObj)
{
bool shouldToggle = GetToggleSelection(obj);
if (shouldToggle)
{
listBoxObj.PreviewMouseLeftButtonDown += ToggleListBox_OnPreviewMouseLeftButtonDown ;
}
else
{
listBoxObj.PreviewMouseLeftButtonDown -= ToggleListBox_OnPreviewMouseLeftButtonDown ;
}
}
}
private static void ToggleListBox_OnPreviewMouseLeftButtonDown (object sender, MouseButtonEventArgs e)
{
// I have a special extension for GetParent, numerous examples on the internet of how you would do that
var lbi = ((DependencyObject) e.OriginalSource).GetParent<ListBoxItem>();
if (lbi != null && lbi.IsSelected)
{
lbi.IsSelected = false;
e.Handled = true;
}
}
}
然后在代码中:
<ListBox ... yourNamespace:ListBoxEx.ToggleSelection="True" />
相关信息:
附加属性帮助:Microsoft 文档链接
GetParent 实用程序示例:MSDN 链接