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
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.
Make the underlying object expose a RemoveCommand
property, and bind the button's Command
property to it. This simplifies the data template; it also greatly simplifies the case where application logic may dictate that a specific item can't be removed.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With