views:

1950

answers:

2

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?");
}
+4  A: 

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);
}
bendewey
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.
John Noonan
+1  A: 

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.

Denis Troller
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.
John Noonan