WPF ListBoxItems with DataTemplates - How do I reference the CLR Object bound to ListBoxItem from within the DataTemplate?
Asked Answered
F

2

9

I have a ListBox, that's bound to an ObservableCollection.

Each ListBoxItem is displayed with a DataTemplate. I have a button in my DataTemplate, that when clicked, needs a reference to the member of the ObservableCollection it's part of the DataTemplate for. I can't use the ListBox.SelectedItem property because the item does not become selected when clicking the button.

So either: I need to figure out how to properly set ListBox.SelectedItem when the mouse hovers, or when the button is clicked. Or I need to figure out another way to get a reference to the CLR Object bound to the ListBoxItem that the button belongs to. The second option seems cleaner, but either way is probably OK.

Simplified code segment below:

XAML:

<DataTemplate x:Key="postBody">
    <Grid>
        <TextBlock Text="{Binding Path=author}"/>
        <Button Click="DeleteButton_Click">Delete</Button>
    </Grid>
</DataTemplate>

<ListBox ItemTemplate="{StaticResource postBody}"/>

C#:

private void DeleteButton_Click(object sender, RoutedEventArgs e)
{
    Console.WriteLine("Where mah ListBoxItem?");
}
Flabellate answered 19/4, 2009 at 20:1 Comment(0)
B
12

Generally speaking people will be interested in a CLR object directly bound to the ListBoxItem, not the actual ListBoxItem. If you had a list of posts for example you could use your existing template of:

<DataTemplate x:Key="postBody" TargetType="{x:Type Post}">
  <Grid>
    <TextBlock Text="{Binding Path=author}"/>
    <Button Click="DeleteButton_Click">Delete</Button>
  </Grid>
</DataTemplate>
<ListBox ItemTemplate="{StaticResource postBody}" 
  ItemSource="{Binding Posts}"/>

and in your code-behind, your Button's DataContext is equal to your DataTemplate's DataContext. In this case a Post.

private void DeleteButton_Click(object sender, RoutedEventArgs e){
  var post = ((Button)sender).DataContext as Post;
  if (post == null)
    throw new InvalidOperationException("Invalid DataContext");

  Console.WriteLine(post.author);
}
Boss answered 19/4, 2009 at 20:13 Comment(1)
This is perfect, and you've managed to also fix the wording of my question. I will edit it as you're right, I'm interested in the bound CLR object, not the ListBoxItem itself.Flabellate
H
3

You have several possibilities, depending on what you need to do.

First, the main question is: "why do you need this"? Most of the time, there is no real use for a reference to the container item (not saying this is your case, but you should elaborate). If you are databinding your listbox, there is rarely a case for that.

Second, you can get the item from the Listbox, using myListBox.ItemContainerGenerator.ContainerFromItem(), provided your listbox is named MyListBox. From the sender parameter, you can get back the actual item that was templated through, for example (where XXX is the type of you databound data):

Container = sender as FrameworkElement;
if(sender != null)
{
    MyItem = Container.DataContext as XXX;
    MyElement = MyListBox.ItemContainerGenerator.ContainerFromItem(MyItem); // <-- this is your ListboxItem.
}

You can find an example an this blog. She uses the index method, but the Item method is similar.

Herthahertz answered 19/4, 2009 at 20:22 Comment(1)
Good answer to the question as originally worded. bendewey correctly assumed I didn't mean what I said I did, but this is also appreciated. Voted up.Flabellate

© 2022 - 2024 — McMap. All rights reserved.