Question

I have added a DataTemplate to a ListBox class to bind my collection to:

<ListBox x:Name="lstEmails" Height="259" Margin="12,0,12,41" Width="276"
         SelectionChanged="lstEmails_SelectionChanged">
    <ListBox.ItemTemplate> 
        <DataTemplate> 
            <StackPanel Orientation="Horizontal"> 
                <Label Visibility="Hidden" Content="{Binding ID}"></Label> 
                <TextBox Width="200"  Text="{Binding EmailAddress}"></TextBox> 
            </StackPanel> 
        </DataTemplate> 
    </ListBox.ItemTemplate> 
</ListBox> 

This does exactly what I want it to do. Although when I click on the TextBox, the ListBox does not automatically set the associated ListItem as Selected. I could do this in code, but I would prefer to use this as a component (no surprises there then).

Any ideas on how to achieve this?


That doesn't seem to work, it won't let me click on anything. Have I missed something. Here is my new XAML.

<UserControl.Resources> 
    <!--<TextBox x:Key="TB" x:Name="TextBoxInsideListBoxItemTemplate"> 
        <TextBox.Style>--> 
            <Style TargetType="{x:Type TextBox}"> 
                <Setter Property="IsHitTestVisible" Value="False" /> 
                <Style.Triggers> 
                    <DataTrigger Binding="{Binding IsSelected, RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type ListBoxItem}, AncestorLevel=1}}" 
                                                     Value="True"> 
                        <Setter Property="IsHitTestVisible" Value="True" /> 
                    </DataTrigger> 
                </Style.Triggers> 
            </Style> 
        <!--</TextBox.Style> 
    </TextBox>--> 
</UserControl.Resources> 
<Grid> 
    <ListBox x:Name="lstEmails" Height="259" Margin="12,0,12,41" Width="276" SelectionChanged="lstEmails_SelectionChanged">
        <ListBox.ItemTemplate> 
            <DataTemplate> 
                <StackPanel Orientation="Horizontal"> 
                    <!--<Label Visibility="Hidden" Content="{Binding ID}"></Label>--> 
                    <TextBox Width="220" Text="{Binding EmailAddress}" > 
                    </TextBox> 
                    <!--<TextBox Width="220" Text="{Binding EmailAddress}" GotFocus="TextBox_GotFocus"></TextBox>-->
                </StackPanel> 
            </DataTemplate> 
        </ListBox.ItemTemplate> 
    </ListBox> 
    <Button Width="20" Margin="12,0,0,12" Name="btnAdd" VerticalAlignment="Bottom" Click="btnAdd_Click" Height="23" HorizontalAlignment="Left">+</Button>
    <Button Width="20" HorizontalAlignment="Left" Margin="30,0,0,12" Name="btnRemove" VerticalAlignment="Bottom" Click="btnRemove_Click" Height="23">-</Button>
    <Button Height="23" HorizontalAlignment="Right" Margin="0,0,12,12" Name="btnApply" VerticalAlignment="Bottom" Width="49" Click="btnApply_Click">Apply</Button>
</Grid>

I think the click twice bit is good functionality.

Was it helpful?

Solution

If you have multiple instance of ListBox then you may consider using your custom listbox (by deriving it from ListBox). See the explanation here.


Or, use this hack if you have only 1 (or only small number of) such ListBox and don't want to create a separate class for that:

<TextBox x:Name="TextBoxInsideListBoxItemTemplate" ... >

    <TextBox.Style>
        <Style TargetType="{x:Type TextBox}">
            <Setter Property="IsHitTestVisible" Value="False" />
            <Style.Triggers>
                <DataTrigger
                        Binding="{Binding IsSelected, RelativeSource={RelativeSource FindAncestor, 
                    AncestorType={x:Type ListBoxItem}, AncestorLevel=1}}"
                        Value="True">
                    <Setter Property="IsHitTestVisible" Value="True" />
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </TextBox.Style>

</TextBox>

Note that you'll have to click once again to edit text in the TextBox (which is actually cool according to me).

OTHER TIPS

You can trigger on the property IsKeyboardFocusWithin in the ItemContainerStyle and set IsSelected to true.

<ListBox.ItemContainerStyle>
    <Style TargetType="{x:Type ListBoxItem}">
        <Style.Triggers>
            <DataTrigger Binding="{Binding IsKeyboardFocusWithin, RelativeSource={RelativeSource Self}}" Value="True">
                <DataTrigger.EnterActions>
                    <BeginStoryboard>
                        <Storyboard>
                            <BooleanAnimationUsingKeyFrames Storyboard.TargetProperty="(ListBoxItem.IsSelected)">
                                <DiscreteBooleanKeyFrame KeyTime="0" Value="True"/>
                            </BooleanAnimationUsingKeyFrames>
                        </Storyboard>
                    </BeginStoryboard>
                </DataTrigger.EnterActions>
            </DataTrigger>
        </Style.Triggers>
    </Style>
</ListBox.ItemContainerStyle>

You could also use a Setter instead of a single frame animation but then the selection will be lost again once the focus leaves the ListBox:

<ListBox.ItemContainerStyle>
    <Style TargetType="{x:Type ListBoxItem}">
        <Style.Triggers>
            <DataTrigger Binding="{Binding IsKeyboardFocusWithin, RelativeSource={RelativeSource Self}}" Value="True">
                <Setter Property="IsSelected" Value="True"/>
            </DataTrigger>
        </Style.Triggers>
    </Style>
</ListBox.ItemContainerStyle>

I had a situation where the selection of a listbox item would change its layout, so the control might have moved away from the cursor before the mouse button is released. I have found no better solution than to use a slight delay in the Storyboard if I want to keep everything in xaml.

More importantly, GotKeyboardFocus seems to work better than IsKeyboardFocusWithin for repeated selections.

<EventTrigger RoutedEvent="GotKeyboardFocus">
    <BeginStoryboard>
        <Storyboard>
            <BooleanAnimationUsingKeyFrames Storyboard.TargetProperty="IsSelected">
                <DiscreteBooleanKeyFrame KeyTime="00:00:00.3" Value="True"/>
            </BooleanAnimationUsingKeyFrames>
        </Storyboard>
    </BeginStoryboard>
</EventTrigger>
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top