1

I have a ListBox defined as follows:

<ListBox
    DisplayMember="Name"
    ItemsSource="{Binding Persons}"
    ItemTemplate="{StaticResource PeopleDataTemplate}"
    SelectedItem="{Binding SelectedItem, Mode=TwoWay}"
    Name="listOfPeople">
</ListBox>

The Person class contains two properties:

public string FirstName { get; set; }
public string LastName { get; set; }

XAML:

<UserControl.Resources>
    <DataTemplate x:Key="PeopleDataTemplate">
        <StackPanel>
            <Image
                Width="75"
                Height="75"
                Source="{Binding Picture}">
            </Image>
            <TextBlock FontSize="14" TextWrapping="Wrap" Text="{Binding Name}" />
            <Button Command="{Binding DataContext.AddCommand , ElementName=main}" Content="Add" />
        </StackPanel>
    </DataTemplate>
</UserControl.Resources>

If I click the Image, the ListBoxItem gets selected but if I click the Button, the ListBoxItem does not get selected. Why?

BionicCode
  • 1
  • 4
  • 28
  • 44
Bogdan
  • 21
  • 4
  • *"The listBoxItem is defined like this"* - this doesn't looks like listbox item. Another thing: `dxe:` namespace is likely to be devexpress, consider to add correct library tag to the question. I've got a feeling your native language is not English (questions you are asking are pretty unclear). There are localized SO sites (e.g. [Russian](https://ru.stackoverflow.com/)), consider to ask question there. – Sinatr Aug 10 '20 at 07:45

2 Answers2

2

The simplest solution is to add corresponding triggers to the ListBox.ItemContainerStyle to delegate focus:

<ListBox>
  <ListBox.ItemContainerStyle>
    <Style TargetType="ListBoxItem">
      <Style.Triggers>
        <Trigger Property="IsKeyboardFocusWithin" Value="True">
          <Setter Property="IsSelected" Value="True" />
        </Trigger>
      </Style.Triggers>
    </Style>
  </ListBox.ItemContainerStyle>
</ListBox>
thatguy
  • 21,059
  • 6
  • 30
  • 40
BionicCode
  • 1
  • 4
  • 28
  • 44
1

Why doesn't the item get selected?

TL;DR: because the mouse down event is handled by the Button and never reaches the ListBoxItem.

What triggers the IsSelected property on each ListBoxItem is the MouseRightButtonDownEvent routed event declared on UIElement whose handling is overriden by ListBoxItem here. However, this MouseDownEvent is a bubbling routed event, meaning that it climbs up the visual tree until marked as handled by something on its way:

  • If you click your Image: Image receives MouseDown but does not handle it. It bubbles up to StackPanel, same story. Bubbles up to ListBoxItem. Bingo, it is handled there and calls a method on the parent ListBox that marks the item as selected.
  • If you click your Button however: the Button receives the MouseDown event and immediately marks it as Handled here. The event propagation is stopped and the event never bubbles up to the ListBoxItem which never gets selected.

How to get around this?

The MVVM way

In your AddCommand implementation, add some code to change the IsSelected property to true. Since you bind to IsSelected, this will select your ListBoxItem.

The XAML way

Add a Click event handler to the Button to manually mark its parent ListBoxItem as Selected.

As pointed out by @Sinatr, you could also react to focus events to select any item that gets the focus. Subscribe to the GotFocus event of each of your StackPanel for example and find the parent ListBox (on which you call UnselectAll()) and find the parent ListBoxItem (on which you call Focus()).

Corentin Pane
  • 4,794
  • 1
  • 12
  • 29
  • After reading your answer I was able to understand the problem of OP. Just adding a bit: if button is inside listbox item template, then it will be also possible to set focus on it without changing listbox selection (which is looking weird). Instead of proposed change to a command a better way is to simply handle focus: anything inside should select parent listbox item on focus. This can be organized as reusable attached behavior. – Sinatr Aug 10 '20 at 07:59
  • okay, without getting into attached behavior for now, how would you go in this case? Subscribe to an OnFocus event for example? – Corentin Pane Aug 10 '20 at 08:01
  • 1
    By subscribing to `GotFocus` event and [finding](https://stackoverflow.com/q/636383/1997232) parent listbox (to call `UnselectAll()`) and listbox item (to call its `Focus()`). – Sinatr Aug 10 '20 at 08:03