OpenLightGroup Blog

rss

Blogs from OpenLightGroup.net


Blend 4: TreeView SelectedItemChanged using MVVM

image

You may find this post helpful if:

  • You are using MVVM 
  • You are using a TreeView control
  • You want the SelectedItem of the TreeView control to be passed to another object

There are a lot of improvements in Expression Blend 4 that makes wiring this up easy. I poked around for a bit until I found the easiest method to do this that I could find.

 

image

Create a new Expression Blend 4 (or higher) project.

image

Add the following folders to the project:

  • Classes
  • Models
  • ViewModels

 

MVVM Support Class

 

We need to add one simple class to support Commanding to allow all this to work.

 

image

Right-click on the Classes folder and select Add New Item…

image

Add a class called DelegateCommand.cs and click OK.

Replace all the code with the following code:

using System.Windows.Input;
using System;
// From http://johnpapa.net/silverlight/5-simple-steps-to-commanding-in-silverlight/
public class DelegateCommand : ICommand
{
    Func<object, bool> canExecute;
    Action<object> executeAction;
    bool canExecuteCache;
    public DelegateCommand(Action<object> executeAction, Func<object, bool> canExecute)
    {
        this.executeAction = executeAction;
        this.canExecute = canExecute;
    }
    #region ICommand Members
    public bool CanExecute(object parameter)
    {
        bool temp = canExecute(parameter);
        if (canExecuteCache != temp)
        {
            canExecuteCache = temp;
            if (CanExecuteChanged != null)
            {
                CanExecuteChanged(this, new EventArgs());
            }
        }
        return canExecuteCache;
    }
    public event EventHandler CanExecuteChanged;
    public void Execute(object parameter)
    {
        executeAction(parameter);
    }
    #endregion
}

This class allows us to easily use Commanding, that will allow us to call a method in our ViewModel.

 

The Model

 

In the Models folder, add a class called SilverlightFolders.cs and replace the code with the following code:

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
public class SilverlightFolder
{
    private ObservableCollection<SilverlightFolder> _SubFolders;
    public ObservableCollection<SilverlightFolder> SubFolders
    {
        get
        {
            if (_SubFolders == null)
            {
                _SubFolders = new ObservableCollection<SilverlightFolder>();
            }
            return _SubFolders;
        }
        set
        {
            _SubFolders = value;
        }
    }
    public string FolderName { get; set; }
    public string FolderPath { get; set; }
}

 

This is a simple class that allows us to create a collection of nested folders. Note that it implements ObservableCollection so that changes to values stored in the class, will cause a notification to any UI element that is bound to it so it can automatically update.

In the Models folder, add a class called DataGenerator.cs and replace the code with the following code:

 

using System;
using System.Net;
using System.Windows;
using System.Collections.ObjectModel;
public static class DataGenerator
{
    public static ObservableCollection<SilverlightFolder> SilverlightFolders()
    {
        return new ObservableCollection<SilverlightFolder>()
            {
                new SilverlightFolder() 
				{ 
					FolderName = "Folder1", FolderPath ="Path1", 
                    SubFolders = new ObservableCollection<SilverlightFolder>()
                    {
						new SilverlightFolder()
							{ 
								FolderName = "Folder1-1", FolderPath ="Path2", 
                                SubFolders = null
							},
							new SilverlightFolder()
							{ 
								FolderName = "Folder1-2", FolderPath ="Path3", 
                                SubFolders = null
							}
					}
				}
			};
    }
}

This class creates some sample data. This class could be altered to retrieve an actual folder structure using web services, for example.

 

The ViewModel

 

In the ViewModels folder, add a class called MainViewModel.cs and replace the code with the following code:

 

using System;
using System.ComponentModel;
using System.Windows.Input;
using System.Collections.ObjectModel;
namespace MVVMTreeViewSelectedItemChanged
{
    public class MainViewModel : INotifyPropertyChanged
    {
        public MainViewModel()
        {
            // Set the command property
            SetProductsCommand = new DelegateCommand(SetProducts, CanSetProducts);
            // Set Sample Tree Data
            SilverlightFolders = DataGenerator.SilverlightFolders();
        }
        public ICommand SetProductsCommand { get; set; }
        public void SetProducts(object param)
        {
            SilverlightFolder objSilverlightFolder = (SilverlightFolder)param;
            this.ViewModelProperty = objSilverlightFolder.FolderName;
        }
        private bool CanSetProducts(object param)
        {
            return true;
        }
        private ObservableCollection<SilverlightFolder> _SilverlightFolders;
        public ObservableCollection<SilverlightFolder> SilverlightFolders
        {
            get { return _SilverlightFolders; }
            private set
            {
                if (SilverlightFolders == value)
                {
                    return;
                }
                _SilverlightFolders = value;
                this.NotifyPropertyChanged("SilverlightFolders");
            }
        }
        private string viewModelProperty = "";
        public string ViewModelProperty
        {
            get
            {
                return this.viewModelProperty;
            }
            set
            {
                this.viewModelProperty = value;
                this.NotifyPropertyChanged("ViewModelProperty");
            }
        }
        #region INotifyPropertyChanged
        public event PropertyChangedEventHandler PropertyChanged;
        private void NotifyPropertyChanged(String info)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(info));
            }
        }
        #endregion
    }
}

 

This class exposes the list of Folders as well as a ViewModelProperty that will hold the TreeView selected value. It also contains the SetProducts method that will be triggered in the UI when the TreeView selected value is changed.

Note that this class implements INotifyPropertyChanged to allow the UI to automatically be updated when values change in the ViewModel

At this point you must build the project for the classes to be used in the following steps.

 

The Fun Part

 

Imagine that a programmer created the previous files. This project can now be turned over to a designer to actually create the UI.

 

image

 

Double-click on MainPage.xaml in the Projects window to open it. In my example I right-clicked on the LayoutRoot to change it to a Canvas Panel and I re-sized it to make it smaller.

 

image

Click on LayoutRoot in the Objects and Timeline window, and in the Properties window, type “DataContext” in the Search box. Next, Click the New button next to DataContext.

 

image

Select MainViewModel and click OK.

image

 

Click the Data tab and expand MainViewModel (under the Data Context section).

 

image

Click on ViewModelProperty and drag it onto the design canvas.

 image

You will need to hover the mouse on the edge of the TextBlock control that is created, and click and drag it to make it wider.

image

Now, click and drag the SilverlightFolders collection to the canvas.

 

image

Position it under the TextBlock control. A TreeView control will be created.

image

 

In the Objects and Timeline window, right-click on the TreeView control and select Edit Additional Templates > Edit Generated Items (Item Template) > Edit Current.

 

image

 

This will take you to the Template editing mode. Select the lower TextBlock (that the file Path is bound to) and click the delete button on your keyboard, to delete it.

 

image

 

Click the Return Scope icon, in the Objects and Timeline window, to return to normal design mode.

 

image

 

Hit the F5 key on your keyboard to compile and run the project. The TreeView control will show the Folder structure but when you click on a Folder name nothing will happen.

 

Setting SelectedItemChanged in the TreeView

 

Now to the point of this Blog post, how to set the SelectedItemChanged value when you click an element on the TreeView control. Also, how to pass the value selected in the TreeView control to the text box that was placed above the TreeView.

 

image

Click on the Assets button on the Tools window.

image

Type “InvokeCommand” in the search box and the InvokeCommandAction behavior will show. (if you don’t see this, install the Silverlight 4 SDK). Drag and drop it on the TreeView control (either in the Objects and Timeline window or on the design canvas).

image

The Behavior will show under the TreeView control in the Objects and Timeline window.

image

Click on the Behavior in the Objects and Timeline window, and in the Properties window, clear out the Search box. You will now see all the properties.

Set the EventName to SelectedItemChanged (so that the Behavior will fire when the selected item is changed in the TreeView).

Click the Data bind icon next to Command (under Common Properties).

 

image

 

  • Select the Data Context tab
  • Select SetProductsCommand (under MainViewModel)
  • Click OK

This instructs the Behavior to call the SetProductsCommand in the ViewModel.

image

 

Click the Advanced options box next to CommandParameter (under Common Properties).

image

Select Data Binding…

image

 

  • Select the Element Property tab
  • Select [TreeView] (in the Scene elements window)
  • Select SelectedItme (in the Properties window)
  • Click OK

This instructs the Behavior to pass the SilverlightFolder object, that the currently selected TreeView item is bound to, to the SetProductsCommand in the ViewModel.

 

image

Hit the F5 key on your keyboard to compile and run the project. The TreeView control will show the Folder structure, and when you click on a Folder name it will show the selected Folder name in the TextBlock.

Download the code at this link.





Comments are closed.
Showing 8 Comments
Avatar  Ivan 6 years ago

thanks!

Avatar  Younus 7 years ago

Thanks

Avatar  Eduardo Puebla 7 years ago

Please, I need binding TreeView using MVVM light with MEF

Avatar  Piet Koole 7 years ago

Very usefull article. I do not agree with Ron. Most of the time there is no designer in our team, and I think most of the developer teams in small companies. With this approach its a lot faster than just code in VS. Thanks for your help.

Avatar  Michael Washington 7 years ago

@Ron Richins - I do create a rough UI rather than just give the designer a blank page. However, the designers ARE able to use Expression Blend to do the UI. This has been proven over several projects.

Avatar  Ron Richins 7 years ago

Loved your article, until &quot;Now for the Fun Part&quot;.&lt;br&gt;&lt;br&gt;Let me set this up, a programmer releases the example as described, it is turned over to the design team (reluctantly) using expression blend 4 (not thier first choice), and you really believe that the remaining steps in this sample are in thier domain of expertise?&lt;br&gt;&lt;br&gt;Ron

Avatar  Turibbio 7 years ago

Very useful!

Avatar  VB 8 years ago

Thank you,, looking for something not button Click ICommand example.&lt;br&gt;Very useful.&lt;br&gt;