WPF ListBox DataContextChanged triggers SelectionChanged - wpf

I have a user control containing an expander. The content of the explander is a ListBox bound to an object, and a DataTemplate displays it correctly. The problem is this: the user can select a Listbox item, and the SelectionChanged handler changed the DataContext of the ListBox to the selected object.
Like this:
<ListBox
Name="RelativesLB" ItemsSource="{Binding Relatives}",
ItemsTemplate ="{...}",
Selectionchanged="Relatives_OnSelectionChanged" />
And:
Relatives_OnSelectionChanged(object sender, ...EventArgs e)
{
var who = (sender as ListBox).SelectedItem as Person;
if (who == null)
return;
People.DataContext = who;
Here is the problem:
The SelectionChanged event fires.
The DataContext is changed, and the ListBox repopulates.
The SelectionChanged event fires with SelectedItem = null. Here, my code does not change the DataContext; it just returns.
the SelectionChanged event fires again with SelectedItem = <whatever is first>. Here, my code changes the DataContext again to that item I don't want this bit. Actually, I want to stop after 2.
the Datacontext is changed to <whatever is first>
...
and so on, until we get an empty Person.Relatives, then we stop.
What I want is the stop after the first DataContext change. You select a person from the Relatives collection, and get the view for that person.
How can I stop the subsequent SelectionChanged events firing?

I guess, in your on Relatives_OnSelectionChanged you need to set
e.Handled = True;

Related

WPF ComboBox SelectionChanged event firing twice

In my DataGrid I am using DataGridComboBoxColumn as follows. Its SelectionChanged event (defined below) always fires twice - once when I click on an item, and then again when I select the new item from the dropdown. When I click on the item that I want to change the SelectionChanged event fires and shows the old value, and then when I select on a new value it fires again and correctly show the new value. But I want the event to be fired only when I select a new value for the combobox.
Question: What is causing this behavior and how can the issue be fixed?
Remark: Many users online seem to have similar issues posted here but none of them helped resolve my issue - maybe, the context is a bit different here. Moreover, the XAML and the code seem ok as it correctly displays the combobox values along with the correctly combox selected values for each row in the grid. Plus, the SelectionChanged event does correctly show the newly selected value but when it fires the second time. Similar code is shown here.
<DataGridComboBoxColumn Header="StartTime" SelectedItemBinding="{Binding localTime}" ItemsSource="{StaticResource localTimeList}">
<DataGridComboBoxColumn.EditingElementStyle>
<Style TargetType="ComboBox">
<EventSetter Event="SelectionChanged" Handler="MyComboBoxColumn_SelectionChanged"/>
</Style>
</DataGridComboBoxColumn.EditingElementStyle>
</DataGridComboBoxColumn>
The event:
private void MyComboBoxColumn_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ComboBox comboBox = (ComboBox)sender;
var selectedVal = comboBox.SelectedValue.ToString();
}
What is causing this behavior?
The SelectionChanged event is raised initially when you enter the edit mode and the SelectedItem property is being bound to your source property.
How can the issue be fixed?
The easiest way to handle this is to check whether the ComboBox has been loaded and simply return from the event handler immediately if it hasn't:
private void MyComboBoxColumn_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ComboBox comboBox = (ComboBox)sender;
if (!comboBox.IsLoaded)
return;
//handle an actual selection here...
}

Remove selected rows from a DataGrid when the user clicks on any other control

I want to remove the selected items from a DataGrid when the user clicks on any other control in the UserControl. The grid has selection mode as "Extended".
I thought of oneapproach
LostFocus event on the DataGrid or the same event on DataGridCell:- But this event is called whenever I select any row in the grid. So I can't remove selected items here.
Use FrameworkElement.PreviewGotKeyboardFocusEvent and handle it at the root which is UserControl in your case like this :
<UserControl ... FrameworkElement.PreviewGotKeyboardFocus="FrameworkPreviewGotKeyboardFocus" />
// Handler :
Important is to check for DataGridCells etc, so we use IsDescendantOf() method to check if some element which lies in our DataGrid is having focus.
Take note of e.Handled and set it's value accordingly.
private void FrameworkPreviewGotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
if (sender is FrameworkElement)
{
Debug.WriteLine(((FrameworkElement)e.OriginalSource).ToString());
if (!((FrameworkElement)e.OriginalSource).IsDescendantOf(MyDataGrid))
{
Debug.WriteLine("Datagrid lost focus completely !");
//e.Handled = true;
// Do something now
}
}
}

Get event trigger

I am using WPF ListBox, binding the ItemsSource to an ObservableCollection.
I subscribed to the SelectionChanged event, which will notify me when the user select/deselect any ListItem.
Now, can I get whether the selection in the ListBox was changed due to user click or Collection change (i.e. items were removed from the Collection which were selected in the ListBox) ??
As you know when you remove some items from the collection, you could set a bool flag as you remove something and then ignore calls to the handler when the flag is true... of course, don't forget to set the flag to false again afterwards:
isProgramAction = true;
Items.Remove(item);
isProgramAction = false;
...
private void SelectionChanged(object sender, RoutedEventArgs e)
{
if (!isProgramAction)
{
// User Action
}
}

ListBox fires lost focus event when lits ListBoxItem is selected, how to avoid this ?

ListBox fires lostfocus event when its ListBoxItem is selected, how to avoid this ?
I want LostFocus to fire only when something else outside it receives focus.
The thing is, ListBox never gets the focus, only it`s items do, so for this to work we`ll have to use them. Probably there`s some elegant solution for this, but here`s my take.
This I placed in code behind of the Window that contains ListBox:
private void myListBox_LostFocus(object sender, RoutedEventArgs e)
{
var focused = FocusManager.GetFocusedElement(this);
var item = focused as ListBoxItem;
if (item == null || !lbMain.Items.Contains(item.DataContext))
{
//do stuff
}
}
The second check in if statement is in case other ListBoxes are present. If your scenario is more complex you most probably will have to add some tweaks: for example if two ListBoxes have the same ItemsSource.
try to set on ListBoxItem the property Focusable to False. Eventually on elements inside your ListBoxItem DataTemplate.

SelectionChanged of a child ListBox

I have a ListBox bound to an ObservableCollection with an ItemTemplate that contains another ListBox. First of all, I tried to get the last selected item of all the listboxes (either the parent and the inner ones) from my MainWindowViewModel this way:
public object SelectedItem
{
get { return this.selectedItem; }
set
{
this.selectedItem = value;
base.NotifyPropertyChanged("SelectedItem");
}
}
So, for example, in the DataTemplate of the items of the parent ListBox I've got this:
<ListBox ItemsSource="{Binding Tails}"
SelectedItem="{Binding Path=DataContext.SelectedItem, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"/>
The problem now, is that when I select an item from the parent ListBox and then an item from a child listbox, I get this:
http://i40.tinypic.com/j7bvig.jpg
As you can see, two items are selected at the same time. How can I solve that?
Thanks in advance.
I have already solved this issue by registering a ClassHandler for the SelectedEvent of the ListBox control.
I just added this in the constructor of my MainWindow class:
EventManager.RegisterClassHandler(typeof(ListBox),
ListBox.SelectedEvent,
new RoutedEventHandler(this.ListBox_OnSelected));
That way, my ListBox_OnSelected event handler will be called whenever a listbox is called, and before the event handlers of the control itself are called.
In the MainWindowViewModel I have a property called SelectedListBox that keeps track of which one is selected:
public System.Windows.Controls.ListBox SelectedListBox
{
get { return this.selectedListBox; }
set
{
if (this.selectedListBox != null)
{
this.selectedListBox.UnselectAll();
}
this.selectedListBox = value;
}
}
Why not using a simple SelectionChanged event handler? Because in the above code, every time you unselect a listbox it raises again the same event, getting an infinite loop of events that fortunately WPF is able to stop.

Resources