添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
相关文章推荐
从未表白的香菇  ·  Arduino ...·  8 月前    · 
飘逸的萝卜  ·  Understanding ...·  1 年前    · 
Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Learn more about Collectives

Teams

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Learn more about Teams

I have the following Data Template applied to a ListBox:

<DataTemplate x:Key="MyTemplate" DataType="{x:Type DAL:Person}">
    <StackPanel Orientation="Horizontal">
        <Button Content="X" Command="{x:Static cmd:MyCommands.Remove}"/>
        <TextBlock Text="{Binding Person.FullName}" />
    </StackPanel>
</DataTemplate>

When I click on the button the command gets fired but the ListBoxItem doesn't get selected. How do I force it to get selected, so that I can get the selected item in my "executed" method?

Thanks

I could pass the button through the command, and find the button on the other side, but this is kind of a hack, I feel... – Gus Cavalcanti Sep 15, 2010 at 19:09 Well, I deleted my answer (i.e. add a trigger that selects the ListBoxItem when keyboard focus is within) because on second thought, it may not be the best way to handle this in most cases. – ASanch Sep 15, 2010 at 19:31 Same here. My initial solution was to bind "Self" to CommandParameter, and on "executed" method I then get the LisBoxItem as follows: ListBoxItem selectedListBoxItem = ((ListBoxItem) MyListBox.ContainerFromElement((DependencyObject) e.Parameter)); But I don't think this is very clean... – Gus Cavalcanti Sep 15, 2010 at 20:34

A better way, since you're not really interested in selecting the item (because it will quickly get deleted anyway) would be to pass the item itself to the Command as a CommandParameter.

Alternatively, you can go about in a roundabout manner either with code-behind or with triggers, but I don't think it would be as to the point. For example:

you could handle the ButtonBase.Click event on your listbox, like

<ListBox ButtonBase.Click="lb_Click"

then in your code behind, do this:

private void lb_Click(object sender, RoutedEventArgs e)
    object clicked = (e.OriginalSource as FrameworkElement).DataContext;
    var lbi = lb.ItemContainerGenerator.ContainerFromItem(clicked) as ListBoxItem;
    lbi.IsSelected = true;

That gets the clicked bound item, because the datacontext of the button is inherited from it's templated item, then the actual autogenerated ListBoxItem from the ListBox's ItemContainerGenerator, and sets the IsSelected property to true. I think that's one of the fastest and easiest ways. Also works with multiple ButtonBase-derived objects in the template.

Of course you can also more nicely encapsulate all this (more or less exactly the same) as a reusable Behavior:

public class SelectItemOnButtonClick : Behavior<ListBox>
    protected override void OnAttached()
        base.OnAttached();
        this.AssociatedObject.AddHandler(ButtonBase.ClickEvent, new RoutedEventHandler(handler), true);
    protected override void OnDetaching()
        this.AssociatedObject.RemoveHandler(ButtonBase.ClickEvent, new RoutedEventHandler(handler));
        base.OnDetaching();
    private void handler(object s, RoutedEventArgs e)
        object clicked = (e.OriginalSource as FrameworkElement).DataContext;
        var lbi = AssociatedObject.ItemContainerGenerator.ContainerFromItem(clicked) as ListBoxItem;
        lbi.IsSelected = true;

You can use it like this:

<ListBox xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" ...>
    <i:Interaction.Behaviors>
        <local:SelectItemOnButtonClick />
    </i:Interaction.Behaviors>
</ListBox>

Add error handling code like at least null checks, of course - wouldn't want a simple thing like this bombing your app.

To understand the problem, the button sets the Handled property to true for all the mouse events that act on it (MouseDown/Click) so they aren't being considered by the ListBoxItem. You could also attach the MouseDown event to the ListBox and walk the visual tree upwards until you reach the parent ListBoxItem but that's a lot more tricky... eh if you're curious, you can read this article to know why, basically you'll also encounter FrameworkContentElements (which also respond to MouseDown) so the code will get more complicated, with the upside that anything clicked inside the datatemplate will trigger the ListBoxItem to be selected, regardless of whether it marked the event as handled.

Heh, I also tried to do it exclusively with styles and triggers but it got ugly fast and I lost interest (and lost track of all the... err thingies). Basically it could be solved, I think, but I reaaaly don't think it's worth the bother. Maybe I overlooked something obvious though, don't know.

Hi Alex, thanks for your answer. In this example I need to delete the item, so yes, I could just pass the item to the command. But I would like to learn the simplest way of selecting the item. – Gus Cavalcanti Sep 15, 2010 at 23:04 Good answer. In SL ButtonBase comes from different base and it has no ButtonBase.ClickEvent. – Davut Gürbüz Jan 31, 2015 at 13:16 A very nice solution the one based on ButtonBase.Click event handler, with this approach I was able to use a RadioButton in a ListBox and make it behave as a CheckBox responding to Toggle and Untoggle actions. Thanks a lot !!! – willyMon Jun 20, 2018 at 5:24 At this point, the underlying object would need to be aware of the collection no? that seems bad. – jr3 Jun 17, 2014 at 22:20

Alex, thanks for answer. Your solution with Behavior is great. First solution is not so good because that will work only if you click on specific Button. Here is one more solution that will work on click on arbitrary control form ListBoxItem template:

<ListBox.ItemContainerStyle>
    <Style TargetType="ListBoxItem" 
           BasedOn="{StaticResource {x:Type ListBoxItem}}">
        <Style.Triggers>
            <Trigger Property="IsKeyboardFocusWithin" Value="True">
                <Setter Property="IsSelected" Value="True"/>
            </Trigger>
        </Style.Triggers>
    </Style>
</ListBox.ItemContainerStyle>

That is XAML only approach. I also set BasedOn property just to be sure to not override the current ListBoxItem style.

This will cause the Item to be deselected if the focus is set to another element. Can't use this approach if I want it to stay selected. – Lumo Nov 15, 2017 at 15:34 Yes Lumo, but in original question was used Remove command, so item will be removed anyway and focus after removing is not relevant any more. Purpose of my solution is to select proper Item from ListBox for deletion when you click button which is part of DataTemplate. Remove command will be able to delete proper item of ListBox because the Command will know which item was selected when command is fired. This will also work for 'Edit' and similar buttons inside of data template. So this is the simplest possible solution for asked original question. – Velibor Feb 6 at 11:35

Thanks for contributing an answer to Stack Overflow!

  • Please be sure to answer the question. Provide details and share your research!

But avoid

  • Asking for help, clarification, or responding to other answers.
  • Making statements based on opinion; back them up with references or personal experience.

To learn more, see our tips on writing great answers.