Set ListBoxItem.IsSelected when child TextBox is Focused Set ListBoxItem.IsSelected when child TextBox is Focused wpf wpf

Set ListBoxItem.IsSelected when child TextBox is Focused


There is a read-only property IsKeyboardFocusWithin that will be set to true if any child is focused. You can use this to set ListBoxItem.IsSelected in a Trigger:

<ListBox ItemsSource="{Binding SomeCollection}" HorizontalAlignment="Left">    <ListBox.ItemContainerStyle>        <Style TargetType="{x:Type ListBoxItem}">            <Style.Triggers>                <Trigger Property="IsKeyboardFocusWithin" Value="True">                    <Setter Property="IsSelected" Value="True" />                </Trigger>            </Style.Triggers>        </Style>    </ListBox.ItemContainerStyle>    <ListBox.ItemTemplate>        <DataTemplate>            <TextBox Width="100" Margin="5" Text="{Binding Name}"/>        </DataTemplate>    </ListBox.ItemTemplate></ListBox>


As Jordan0Day correctly pointed out there can be indeed big problems using IsKeyboardFocusWithin solution. In my case a Button in a Toolbar which regards to the ListBox was also not working anymore. The same problem with focus. When clicking the button the ListBoxItem does loose the Focus and the Button updated its CanExecute method, which resulted in disabling the button just a moment before the button click command should be executed.

For me a much better solution was to use a ItemContainerStyle EventSetter as described in this post: ListboxItem selection when the controls inside are used

XAML:

<Style x:Key="MyItemContainer.Style" TargetType="{x:Type ListBoxItem}">    <Setter Property="Background" Value="LightGray"/>    <EventSetter Event="GotKeyboardFocus" Handler="OnListBoxItemContainerFocused" />    <Setter Property="Template">        <Setter.Value>            <ControlTemplate TargetType="{x:Type ListBoxItem}">                <Border x:Name="backgroundBorder" Background="White">                    <ContentPresenter Content="{TemplateBinding Content}"/>                </Border>            <ControlTemplate.Triggers>                 <Trigger Property="IsSelected" Value="True">                     <Setter TargetName="backgroundBorder" Property="Background" Value="#FFD7E6FC"/>                 </Trigger>             </ControlTemplate.Triggers>         </ControlTemplate>     </Setter.Value> </Setter></Style>

EventHandler in the code behind of the view:

private void OnListBoxItemContainerFocused(object sender, RoutedEventArgs e){    (sender as ListBoxItem).IsSelected = true;}


One way to achieve that is by implementing a custom behavior using an attached property. Basically, the attached property would be applied to the ListBoxItem using a style, and would hook up to their GotFocus event. That even fires if any descendant of the control gets the focus, so it is suitable for this task. In the event handler, IsSelected is set to true.

I wrote up a small example for you:

The Behavior Class:

public class MyBehavior{    public static bool GetSelectOnDescendantFocus(DependencyObject obj)    {        return (bool)obj.GetValue(SelectOnDescendantFocusProperty);    }    public static void SetSelectOnDescendantFocus(        DependencyObject obj, bool value)    {        obj.SetValue(SelectOnDescendantFocusProperty, value);    }    public static readonly DependencyProperty SelectOnDescendantFocusProperty =        DependencyProperty.RegisterAttached(            "SelectOnDescendantFocus",            typeof(bool),            typeof(MyBehavior),            new UIPropertyMetadata(false, OnSelectOnDescendantFocusChanged));    static void OnSelectOnDescendantFocusChanged(        DependencyObject d, DependencyPropertyChangedEventArgs e)    {        ListBoxItem lbi = d as ListBoxItem;        if (lbi == null) return;        bool ov = (bool)e.OldValue;        bool nv = (bool)e.NewValue;        if (ov == nv) return;        if (nv)        {            lbi.GotFocus += lbi_GotFocus;        }        else        {            lbi.GotFocus -= lbi_GotFocus;        }    }    static void lbi_GotFocus(object sender, RoutedEventArgs e)    {        ListBoxItem lbi = sender as ListBoxItem;        lbi.IsSelected = true;    }}

The Window XAML:

<Window x:Class="q2960098.MainWindow"        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"        xmlns:sys="clr-namespace:System;assembly=mscorlib"        Title="MainWindow" Height="350" Width="525" xmlns:my="clr-namespace:q2960098">    <Window.Resources>        <DataTemplate x:Key="UserControlItemTemplate">            <Border BorderBrush="Black" BorderThickness="5" Margin="10">                <my:UserControl1/>            </Border>        </DataTemplate>        <XmlDataProvider x:Key="data">            <x:XData>                <test xmlns="">                    <item a1="1" a2="2" a3="3" a4="4">a</item>                    <item a1="a" a2="b" a3="c" a4="d">b</item>                    <item a1="A" a2="B" a3="C" a4="D">c</item>                </test>            </x:XData>        </XmlDataProvider>        <Style x:Key="MyBehaviorStyle" TargetType="ListBoxItem">            <Setter Property="my:MyBehavior.SelectOnDescendantFocus" Value="True"/>        </Style>    </Window.Resources>    <Grid>        <ListBox ItemTemplate="{StaticResource UserControlItemTemplate}"                 ItemsSource="{Binding Source={StaticResource data}, XPath=//item}"                 HorizontalContentAlignment="Stretch"                 ItemContainerStyle="{StaticResource MyBehaviorStyle}">        </ListBox>    </Grid></Window>

The User Control XAML:

<UserControl x:Class="q2960098.UserControl1"             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"              xmlns:d="http://schemas.microsoft.com/expression/blend/2008"              mc:Ignorable="d"              d:DesignHeight="300" d:DesignWidth="300">    <UniformGrid>        <TextBox Margin="10" Text="{Binding XPath=@a1}"/>        <TextBox Margin="10" Text="{Binding XPath=@a2}"/>        <TextBox Margin="10" Text="{Binding XPath=@a3}"/>        <TextBox Margin="10" Text="{Binding XPath=@a4}"/>    </UniformGrid></UserControl>