【发布时间】:2010-05-10 13:13:02
【问题描述】:
我有一个列表框,如果用户尚未完成某些任务,我想防止更改列表框的选择,这是我目前可以提供的最佳解释,在 WinForms 中曾经有选择更改并且它具有可取消的事件争论,我们甚至可以在其中捕获和取消选择。
我以为我会继承列表框并做一些事情,但在内部,Selector 类中的所有功能都被隐藏了,我可以在反射器中看到,但我无法继承和覆盖任何方法!
【问题讨论】:
标签: wpf listbox selectionchanging
我有一个列表框,如果用户尚未完成某些任务,我想防止更改列表框的选择,这是我目前可以提供的最佳解释,在 WinForms 中曾经有选择更改并且它具有可取消的事件争论,我们甚至可以在其中捕获和取消选择。
我以为我会继承列表框并做一些事情,但在内部,Selector 类中的所有功能都被隐藏了,我可以在反射器中看到,但我无法继承和覆盖任何方法!
【问题讨论】:
标签: wpf listbox selectionchanging
我从 ListBox 派生了一个 MyListBox 类,并添加了名为 SelectionChanging 的事件,它是可取消的事件。然后我在 MyListBox 中使用 MyListBoxItem 作为 ItemContainer,它处理 Preview Left Mouse Up 事件并引发选择更改事件,在取消值时,我将事件标记为已处理,这可以防止新的选择以及它允许我通知用户做某事。
【讨论】:
将IsSelected绑定到您的视图模型类中的一个属性,并在属性的setter中处理这种情况,例如:
public bool IsSelected
{
get { return _IsSelected; }
set
{
if (value && DisableSelection)
{
AlertUser();
}
else
{
_IsSelected = value;
}
OnPropertyChanged("IsSelected");
}
}
请注意,即使属性没有更改,您也会引发 PropertyChanged 事件,因为从视图的角度来看,它确实发生了更改。
【讨论】:
PreviewMouseDown事件,并将Handled设置为true。
我知道这并不能直接回答您的问题,但在大多数情况下(我需要有理由不这样做),在满足选择标准之前,我根本无法控制。
这个简单的步骤消除了确定值是否已更改以及是否为有效更改等的大部分复杂性。如果您允许编辑组合框(即输入的值),则会增加另一个级别复杂性。
否则,这里有一个相关的讨论: How to prevent/cancel a combobox's value change in c#?
【讨论】:
一种解决方案是让 ListBox 和 ListBoxItems 不可聚焦,直到您准备好让用户更改它们。这是一个完成此任务的快速模型:
XAML:
<StackPanel>
<ListBox x:Name="LB">
<ListBoxItem Focusable="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBox}}, Path=Focusable}" Content="item 1"/>
<ListBoxItem Focusable="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBox}}, Path=Focusable}" Content="item 2"/>
<ListBoxItem Focusable="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBox}}, Path=Focusable}" Content="item 3"/>
<ListBoxItem Focusable="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBox}}, Path=Focusable}" Content="item 4"/>
<ListBoxItem Focusable="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBox}}, Path=Focusable}" Content="item 5"/>
<ListBoxItem Focusable="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBox}}, Path=Focusable}" Content="item 6"/>
</ListBox>
<Button Content="Lock/Unlock" Click ="Button_Click"/>
</StackPanel>
代码:
private void Button_Click(object sender, RoutedEventArgs e)
{
if (LB.Focusable == true)
LB.Focusable = false;
else
LB.Focusable = true;
}
【讨论】:
这有点小技巧,但我只是使用了列表框的 PreviewMouseLeftButtonDown 事件。
在处理程序中,我必须询问用户是否确定要离开。出于某种原因,在弹出消息框后,无论您是否将 e.handled 标记为 true,都不会触发所选事件,这与该消息框有关。因此,如果用户确认导航,我将使用鼠标单击来查找被单击的相应数据项(使用 VisualTreeHelper 循环,直到找到 ListBoxItem),然后手动选择该项目。如果用户选择不离开,只需将 e.handled 设置为 false(尽管弹出消息框似乎具有相同的效果。)
【讨论】:
当某些条件不满足时,我也有同样的需求(询问用户是否真的想更改 单选 ListBox 中的选定项目)。单选很重要,否则代码会变得更复杂。我的解决方案基于 tempy 的回答,但是遍历可视化树似乎太麻烦了,所以我只是使用 listBox.ItemContainerGenerator 来询问 ListBox 项目是否鼠标悬停在其中任何一个项目上,并采取相应措施:
private void listBox_PreviewMouseDown(object sender, MouseButtonEventArgs e) {
if (CheckIfCurrentlySelectedItemCanBeDeselected())
// let things run normally
return;
var itemUnderMouse = GetListBoxItemUnderMouse(listBox);
// check if there is no item under mouse
// or if it is the currently selected item
if (itemUnderMouse == null || itemUnderMouse.Content == currentItem)
return;
// always set Handled and take care of changing selection manually
e.Handled = true;
if (MessageBox.Show("The selected value is not valid, change selection?", "", MessageBoxButton.YesNo) == MessageBoxResult.Yes) {
// change the value manually
lbArticles.SelectedItem = itemUnderMouse.Content;
}
}
private ListBoxItem GetListBoxItemUnderMouse(ListBox lb) {
foreach (object o in lb.Items) {
ListBoxItem lbi = lb.ItemContainerGenerator.ContainerFromItem(o) as ListBoxItem;
if (lbi != null && lbi.IsMouseOver) {
return lbi;
}
}
return null;
}
【讨论】: