MVVM – ListView SelectedItem and DataBinding

UPDATE: I recommend a new version of this post at following link C# – WPF MVVM DataBinding 2014

On studying how to use MVVM pattern in WPF I found out that the ListView or ListBox control doesn’t support ICommand interface and therefore I can not bind a command to a ListView. After searching on Internet I saw that man can use Prism extensions framework http://www.codeplex.com/CompositeWPF to add custom command to our ListView control. The example below guides step by step how to get SelectedItem of ListView in MVVM pattern using command.

1. Open Visual Studio and create a WPF Application “MVVM WPF ListBox SelectedItem”.
2. Add a class Product which will be our Model.

class Product
{
	public string Name { get; set; }
	public int Price { get; set; }
}

2. Add a class ViewModel which will be our ViewModel. In this class I create a hard-code database of Model which is stored in a ObservableCollection.

class ViewModel
{
	ObservableCollection<Product> m_lstProducts = new ObservableCollection<Product>();

	public ViewModel()
	{
		m_lstProducts.Add(new Product() { Name="Car", Price=1 });
		m_lstProducts.Add(new Product() { Name="Pencil",Price=2});
		m_lstProducts.Add(new Product() { Name="Computer",Price=3});
		m_lstProducts.Add(new Product() { Name="Table",Price=4});
		m_lstProducts.Add(new Product() { Name="Chair",Price=5});
	}
	public ObservableCollection<Product> ProductList
	{
		get	{ return m_lstProducts;	}
	}
}

3. In XAML file, add a ListView to show this list of product. I bind the ListView ItemSource with ProductList and add two columns which bind to Name and Price of product.

<Grid>
	<ListView ItemsSource="{Binding ProductList}" x:Name="lvProducts" Margin="8,8,8,37.96">
		<ListView.View>
			<GridView>
				<GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name}"/>
				<GridViewColumn Header="Price" DisplayMemberBinding="{Binding Price}"/>
			</GridView>
		</ListView.View>
	</ListView>
	<Label HorizontalAlignment="Left" Margin="8,0,0,8" VerticalAlignment="Bottom" Content="Current selected item:"/>
	<Label x:Name="lblSelectedItem" Margin="137.967,0,8,8" VerticalAlignment="Bottom" Height="25.96"/>
</Grid>

4. Set DataContext of our program by an instance of ViewModel

public Window1()
{
	ViewModel vmProduct = new ViewModel();
	this.DataContext = vmProduct;
	InitializeComponent();
}

5. Compile and run program, the ListView will be fulfilled with the database

6. Now I would like that when the user clicks on an item of ListView, its name is shown in label lblSelectedItem. Therefore I bind Content property of lblSelectedItem to a property of ViewModel variable

<Label Content="{Binding CurrentSelected}" x:Name="lblSelectedItem" Margin="137.967,0,8,8" VerticalAlignment="Bottom" Height="25.96"/>

7. So that every time when value of this property was changed, a notification will be sent to update data binding I decide to make ViewModel class to implement INotifyPropertyChanged.

class ViewModel : INotifyPropertyChanged
{
	...
	string m_strCurrent = "";

	....

	private void NotifyPropertyChanged(String info)
	{
		if (PropertyChanged != null)
		{
			PropertyChanged(this, new PropertyChangedEventArgs(info));
		}
	}

	public string CurrentSelected
	{
		get
		{ return this.m_strCurrent; }

		set
		{
			if (value != this.m_strCurrent)
			{
				this.m_strCurrent = value;
				NotifyPropertyChanged("CurrentSelected");
			}
		}

	}

	public event PropertyChangedEventHandler PropertyChanged;
}

8. What I should do at last, is to add command for handling the event when user clicks on item in ListView. As I said above, ListView item doesn’t support ICommand. So let’s download Prism library. From Solution Explorer in VS, add reference to Microsoft.Practices.Composite.dll and Microsoft.Practices.Composite.Presentation.dll. These library locate at *\CAL\Desktop\Composite.Presentation (you need to build the solution). Then add delegate SelectedCommand to handle event when user clicks on items in ListView

class ViewModel : INotifyPropertyChanged
    {
		...

        public ViewModel()
        {
			...
            ProductSelected = new DelegateCommand<Product>(obj =>
                {
                    CurrentSelected = obj.Name;
                });

        }
		...
        public ICommand ProductSelected { get; private set; }
		...
}

9. Bind our ListView to this command in XAML files

<Window x:Class="MVVM_WPF_ListBox_SelectedItem.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:Commands="clr-namespace:MVVM_WPF_ListBox_SelectedItem"
    Title="MVVM WPF ListBox SelectedItem" Height="300" Width="300" WindowStartupLocation="CenterScreen">
    <Grid>
    	<ListView Commands:Selected.Command="{Binding ProductSelected}" ItemsSource="{Binding ProductList}" x:Name="lvProducts" Margin="8,8,8,37.96">
			...
    	</ListView>
    	...
    </Grid>
</Window>

10. You can see that in XAML file, I used a static class Selected to store command. The code of this class is in code snippet below

public static class Selected
    {
        private static readonly DependencyProperty SelectedCommandBehaviorProperty = DependencyProperty.RegisterAttached(
            "SelectedCommandBehavior",
            typeof(SelectorSelectedCommandBehavior),
            typeof(Selected),
            null);

        public static readonly DependencyProperty CommandProperty = DependencyProperty.RegisterAttached(
            "Command",
            typeof(ICommand),
            typeof(Selected),
            new PropertyMetadata(OnSetCommandCallback));

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "Only works for selector")]
        public static void SetCommand(Selector selector, ICommand command)
        {
            selector.SetValue(CommandProperty, command);
        }

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "Only works for selector")]
        public static ICommand GetCommand(Selector selector)
        {
            return selector.GetValue(CommandProperty) as ICommand;
        }

        private static void OnSetCommandCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            var selector = dependencyObject as Selector;
            if (selector != null)
            {
                GetOrCreateBehavior(selector).Command = e.NewValue as ICommand;
            }
        }

        private static SelectorSelectedCommandBehavior GetOrCreateBehavior(Selector selector)
        {
            var behavior = selector.GetValue(SelectedCommandBehaviorProperty) as SelectorSelectedCommandBehavior;
            if (behavior == null)
            {
                behavior = new SelectorSelectedCommandBehavior(selector);
                selector.SetValue(SelectedCommandBehaviorProperty, behavior);
            }

            return behavior;
        }
    }

    public class SelectorSelectedCommandBehavior : CommandBehaviorBase<Selector>
    {
        public SelectorSelectedCommandBehavior(Selector selectableObject)
            : base(selectableObject)
        {
            selectableObject.SelectionChanged += OnSelectionChanged;
        }

        void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            CommandParameter = TargetObject.SelectedItem;
            ExecuteCommand();
        }
    }

Compile and run program, when you click on each item in ListView, its name will be shown in label lblSelectedItem. The complete source code you can download here “MVVM WPF ListBox SelectedItem“.

6 thoughts on “MVVM – ListView SelectedItem and DataBinding”

  1. Hi,
    I am using Commands:Selected.Command for my ComboBox. In XAML I have set IsEnabled = false for the control. But when the following line gets executed,
    “GetOrCreateBehavior(selector).Command = e.NewValue as ICommand;”
    the control’s IsEnabled property changed back to true.

    What causes this behavior?

    Thanks,
    Vivek

  2. Hello,

    The Zip file attached to the link is corrupted.
    Will you be able to attach working version? That will be of great help.

    also, Selected class needs a lot of references, can you atleast provide with those to make this sample work ?

    Thanks in advance.

  3. @Karan: The zip file works. Use Firefox to download it or any download manager. It consists of all needed library in zip file.

  4. Why don’t you use

    for your label which shows the currently selected item? No code-behind or commands are need.

  5. Label Content=”{Binding ElementName=lvProducts, Path=SelectedValue.Name}” x:Name=”lblSelectedItem2″ Margin=”137.967,0,8,8″ VerticalAlignment=”Bottom” Height=”25.96″

    Sry for double post

Leave a Reply

Your email address will not be published. Required fields are marked *