PersuitOfPerfection
PersuitOfPerfection

Reputation: 1029

Getting clicked Item object using XAML databinding

I am binding my ListBox to an items collection, which contains instances of my Item's class.

This part works as expected. The problem i'm having is accessing the clicked ListBox items item instance. I have included the full example and the relevant XAML.

I have also included an example of how I would like to access the Item instance inside the listBoxItems_PreviewMouseDown event handler.

I assume i'm missing something obvious, but the code crashes when I click an item. Am I missing another binding to glue all of this together?

Thank you

Code behind:

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Windows;
using System.Windows.Threading;

namespace WpfApplication1
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        // Item class
        internal class Item : INotifyPropertyChanged
        {
            public Item(string name = null)
            {
                this.Name = name;
            }

            public string Name { get; set; }
            public string ImagePath { get; set; }
            public string SomeString { get; set; }
            public int SomeInt { get; set; }

            public event PropertyChangedEventHandler PropertyChanged;
            private void NotifyPropertyChanged(String propertyName)
            {
                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
                }
            }
        }

        // Item collection
        ObservableCollection<Item> ItemsCollection;

        public MainWindow()
        {
            InitializeComponent();

            // Initialize the items collection
            this.ItemsCollection = new ObservableCollection<Item>();

            for (int i = 0; i < 5; i ++)
            {
                Dispatcher.Invoke(new Action(() =>
                {
                    Item newItem = new Item
                    {
                        Name = "test " + i,
                        ImagePath = @"Images\" + i + ".jpg",
                        SomeString = "example" + i,
                        SomeInt = i,
                    };

                    // Add the new item to the collection
                    this.ItemsCollection.Add(newItem);
                }), DispatcherPriority.Background);
            }

            // Set the items source
            this.listBoxItems.ItemsSource = this.ItemsCollection;
        }

        // Handle mouse down events on listbox items
        private void listBoxItems_PreviewMouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            // Ideally, i'd like to do something like this
            Item item = sender as Item;

            // So then I could do for example
            Console.WriteLine(@"Clicked item SomeString: {0}, SomeInt {1}", item.SomeString, item.SomeInt);
        }
    }
}

XAML:

<userControls:MyListBox x:Name="ItemsListBox" ItemsSource="{Binding ItemsCollection}" PreviewMouseDown="listBoxItems_PreviewMouseDown">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <VirtualizingStackPanel>
                <Image Source="{Binding ImagePath}" Width="200" Height="100"/>
                <TextBlock Text="{Binding Name}" />
            </VirtualizingStackPanel>
        </DataTemplate>
    </ListBox.ItemTemplate>
</userControls:MyListBox>

Upvotes: 1

Views: 414

Answers (2)

Leaky
Leaky

Reputation: 3646

Besides the accepted answer, alternatively you can bind the PreviewMouseDown event directly to the ListBoxItem.

In that case the XAML can be (emphasis on the added <ListBox.ItemContainerStyle> node):

<userControls:MyListBox x:Name="ItemsListBox" ItemsSource="{Binding ItemsCollection}" PreviewMouseDown="listBoxItems_PreviewMouseDown">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <VirtualizingStackPanel>
                <Image Source="{Binding ImagePath}" Width="200" Height="100"/>
                <TextBlock Text="{Binding Name}" />
            </VirtualizingStackPanel>
        </DataTemplate>
    </ListBox.ItemTemplate>
    <ListBox.ItemContainerStyle>
        <Style TargetType="{x:Type ListBoxItem}">
            <EventSetter Event="PreviewMouseDown" Handler="listBoxItems_PreviewMouseDown" />
        </Style>
    </ListBox.ItemContainerStyle>
</userControls:MyListBox>

And the handler:

    // Handle mouse down events on listbox items
    private void listBoxItems_PreviewMouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
    {

        // Ideally, i'd like to do something like this
        Item item = (sender as ListBoxItem).DataContext as Item;

        // So then I could do for example
        Console.WriteLine(@"Clicked item SomeString: {0}, SomeInt {1}", item.SomeString, item.SomeInt);
    }

Upvotes: 2

Yarik
Yarik

Reputation: 1578

You can get the OriginalSource of the event and examine its DataContext:

var item = (e.OriginalSource as FrameworkElement)?.DataContext as Item;
if (item != null) { /* Do something with item... */ }

Upvotes: 1

Related Questions