0

I have UserControl containing a procedurally generated ItemsControl. Each item in the ItemsControl contains a ListBox and there is no consistent number of how many items will be generated. The selected item in the listbox is bound to am object (SelectedClass) in the ViewModel. The initial value of the SelectedClass object is null.

The scenario I am running into is this:

  1. User selects ListBoxItemA from ItemsControlItemA, PropertyChanged fires, SelectedClass object is set to the proper value.
  2. User then selects ListBoxItemA from ItemsControlItemB, PropertyChanged fires, SelectedClass object is set to the proper value.
  3. User then selects ListBoxItemA from ItemsControlItemA, but since the selection in that list is still considered to be the same item from step 1, PropertyChanged does not fire, and the SelectedClass object remainsListBoxItemA from ItemsControlItemB.

So my question is, how do i get the UpdateSourceTrigger event to fire OnClick rather than on PropertyChanged, and is that even the best way to approach it? I'm using the MVVM Light framework.

Thanks

<ItemsControl ItemsSource="{Binding AllUpcomingClasses}" >
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <StackPanel>
                <TextBlock Text="{Binding classDescription}" />                    
                <ListBox Name="availableClasses"
                    ItemsSource="{Binding ClassInstances}" 
                    SelectedItem="{Binding
                                       DataContext.SelectedClass, 
                                       Mode=TwoWay}
                                       RelativeSource={RelativeSource 
                                           FindAncestor, 
                                           AncestorType={x:Type UserControl}}">
                    <ListBox.ItemTemplate>
                        <DataTemplate>
                            <StackPanel>
                                <Grid>
                                    <TextBlock Text="{Binding ClassDate}" />
                                </Grid>
                            </StackPanel>
                        </DataTemplate>
                    </ListBox.ItemTemplate>
                </ListBox>                                    
            </StackPanel>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

Edit: Cleaned up the example a bit for readability.

Ryan
  • 127
  • 1
  • 12
  • I think you've got an x/y here, but I'm not sure. A minimal example would definitely help (e.g., the minimum XAML and code required to reproduce the issue, without stuff that doesn't matter like styles and templates). ItemsSource/SelectedItem bindings are particularly sensitive to issues relating to object instances, but I can't tell if that's the problem here. Another thing to try is to specify a TwoWay mode on all SelectedClass bindings. –  Dec 07 '16 at 17:20
  • Cleaned it up a bit for you. TwoWay mode was already set. – Ryan Dec 07 '16 at 17:32
  • 1
    Appreciate, now check *this* edit. Clarity of code helps you get better answers quicker. Having an [sscce](http://www.sscce.org/) was more what I was talking about. –  Dec 07 '16 at 17:59
  • Sounds like your `ListBox`es should handle `GotFocus` `event` thus setting the `SelectedItem` first time they receive focus. After that the Selector will handle the changes. HTH – XAMlMAX Dec 07 '16 at 21:42

1 Answers1

0

You could handle the PreviewMouseLeftButtonDown event of the ListBoxItem container and "manually" set the SelectedItem property of your view model if the clicked item is the one that is already selected:

<ListBox SelectedItem="{Binding SelectedItem}" xmlns:s="clr-namespace:System;assembly=mscorlib">
  <ListBox.ItemContainerStyle>
     <Style TargetType="ListBoxItem">
        <EventSetter Event="PreviewMouseLeftButtonDown" Handler="OnMouseLeftButtonDown"/>
     </Style>
  </ListBox.ItemContainerStyle>
  <s:String>A</s:String>
  <s:String>B</s:String>
  <s:String>C</s:String>
</ListBox>



private void OnMouseLeftButtonDown(object sender, MouseEventArgs e)
{
  ListBoxItem lbi = sender as ListBoxItem;
  if (lbi != null)
  {
    YourViewModel vm = DataContext as YourViewModel;
    if (vm != null)
    {
        var selectedItem = lbi.DataContext as YourObjectType;
        if (vm.SelectedItem == selectedItem)
        {
            vm.SelectedItem = selectedItem;
            e.Handled = false;
        }
    }
  }
}

If you don't want to handle this in the code-behind of the view you could wrap the same functionality in an attached behaviour: https://www.codeproject.com/articles/28959/introduction-to-attached-behaviors-in-wpf. The former approach doesn't really break the MVVM pattern though since you are just kind of "extending" the ListBox control functionality to be able to set the same view model source property that the ListBox control sets for you when you select a new item. This functionality belongs to the view or the control.

mm8
  • 163,881
  • 10
  • 57
  • 88
  • Though I went a different route with the code in the handler, the PreviewLeftMouseButtonDown got me on the right path. – Ryan Dec 27 '16 at 16:28