How to program "Selection Changing" event in ListBox in WPF?
I have a listbox and I want to prevent changing of ListBox's selection if user has not finished certain tasks, this is the best explanati开发者_JAVA百科on i can provide at this time, in WinForms there used to be selection changing and it has cancellable event arguement, where in we could trap and cancel even of changing the selection.
I thought I will inherit listbox and do something but internally all functionalities in Selector class are hidden that I can see in reflector but there is no way i can inherit and override any methods !!
I derived a class MyListBox from ListBox and added event called SelectionChanging which is cancellable event. Then I used MyListBoxItem as ItemContainer in MyListBox which handles Preview Left Mouse Up event and raises Selection Changing event, on cancel value, I mark event as handled, which prevents new selection as well as it allows me to notify user to do someting.
Bind IsSelected
to a property in your view model class, and handle the case in the property's setter, e.g.:
public bool IsSelected
{
get { return _IsSelected; }
set
{
if (value && DisableSelection)
{
AlertUser();
}
else
{
_IsSelected = value;
}
OnPropertyChanged("IsSelected");
}
}
Note that you raise the PropertyChanged
event even if the property didn't change, because from the view's perspective it actually did change.
I know this doesn't directly answer your question, but in most cases (I'd need to be convinced of a reason not to), I would simply not enable to control until the criteria for selection is met.
This simple step removes most of the complexity of figuring out that the value changed, and if it was a valid change, etc. If you allow the comboxbox to be edited (i.e. a value typed in), that adds another level of complexity.
Otherwise, here is a related discussion: How to prevent/cancel a combobox's value change in c#?
One solution would be to make the ListBox and ListBoxItems not focusable until you are ready for the user to change them. Here is a quick mock-up that accomplished this:
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>
Code:
private void Button_Click(object sender, RoutedEventArgs e)
{
if (LB.Focusable == true)
LB.Focusable = false;
else
LB.Focusable = true;
}
This is a bit of a hack, but I just used the listbox's PreviewMouseLeftButtonDown event.
In the handler, I had to ask the user if they were sure they want to navigate away. For some reason, after popping up a messagebox, regardless of whether you mark e.handled as true or not, the selected event will not fire, something about that messagebox. So, if the user confirms the navigation, I would use the mouseclick to find the corresponding dataitem that got clicked on (loop with VisualTreeHelper until you find a ListBoxItem) and then select that item manually. If the user chooses not to navigate away, just set e.handled to false (though popping up a messagebox seems to have an identical effect.)
I had the same need (ask the user if he really wants to change the selected item in a single selection ListBox) when certain criteria are not met. Being single selection is important, otherwise the code gets more complicated. I based my solution off of tempy's answer, but traversing the visual tree seemed like too much trouble, so I just used the listBox.ItemContainerGenerator to ask the ListBox items if the mouse is over any of them, and act accordingly:
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;
}
精彩评论