Silverlight TreeView: MVVM and editing [1]

by Andrej Tozon 20. January 2009 16:13

This is the first post in a series with a simple, yet specific goal: to add editing capabilities to Silverlight Toolkit’s TreeView control and build a MVVM (Model-View-ViewModel) pattern application with it.

This introductory post will deal with really basic stuff. Nothing new and fancy, we’re just going to set up the grounds for a simple application that displays a hierarchical collection of some help topics in a TreeView.

We’ll begin with finding the suitable hierarchical structure to put in the TreeView. For the purpose of this sample, I copied some content from Silverlight SDK documentation:

Silverlight Help Topics

First, we’ll need a data generator that will create the data structure. In a real world scenario, you would call a web service to get such data, but we’ll get to that a bit later. For now, this dummy data generator will have to do:

public static class DataGenerator
{
public ObservableCollection<HelpTopic> GetHelpTopics()
{
return new ObservableCollection<HelpTopic>()
{
new HelpTopic() { Name = "Debugging, Error Handling, and Exceptions", SubTopics =
                new ObservableCollection<HelpTopic>()
{
new HelpTopic() {Name = "Debugging Overview", SubTopics =
                    new ObservableCollection<HelpTopic>()
{
new HelpTopic() {Name = "Walkthrough: Setting Up Remote Debugging on the Macintosh"}
}},
new HelpTopic() {Name = "Error Handling"},
new HelpTopic() {Name = "Exceptions", SubTopics=new ObservableCollection<HelpTopic>()
{
new HelpTopic() {Name = "Exception Class and Properties"},
new HelpTopic() {Name = "Exception Hierarchy"},
new HelpTopic() {Name = "Exception Handling"},
new HelpTopic() {Name = "Best Practices for Handling Exceptions"}
}}
}},
new HelpTopic() { Name = "Deployment and Localization", SubTopics =
                new ObservableCollection<HelpTopic>()
{
new HelpTopic() {Name = "Creating Globally Aware Applications"},
new HelpTopic() {Name = "Localizing Silverlight Applications"}
}},
new HelpTopic() { Name = "Performance", SubTopics = new ObservableCollection<HelpTopic>()
{
new HelpTopic() {Name = "Performance Tips"},
new HelpTopic() {Name = "How To: Use a Background Worker"},
new HelpTopic() {Name = "Threading", SubTopics = new ObservableCollection<HelpTopic>()
{
new HelpTopic() {Name = "Managed Threading Overview"},
new HelpTopic() {Name = "Best Practices for Managed Threading"},
new HelpTopic() {Name = "Threading Objects and Features"}
}},
}}
};
}
}

Looks ugly, but there is really just one entity class involved – here is the HelpTopics class:

public class HelpTopic
{
public string Name { get; set; }
public ObservableCollection<HelpTopic> SubTopics { get; set; }

public HelpTopic()
{
}
}

The name property will hold the text to be displayed in a tree, and SubTopics is a collection of child items. ObservableCollection plays a significant role in MVVM applications because of its ability to inform potential listeners that the collection has changed in some way (item was added, changed, or list was cleared, etc.). With MVVM, one potential listener is the User Interface (visual elements in your application), which can react to any changes, made in data which it’s bound to.
The HelpTopic class currently doesn’t hold any unique Id field. I intentionally left that out for the moment; it will be added when needed.

OK, we have our Model. What we need next is the ViewModel. A ViewModel is a bridge between the View and the Model and acts like a Model to the View. Hence the name :)

Since it’s going to serve the main page, we’ll call it – the PageViewModel:

public class PageViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;

private ObservableCollection<HelpTopic> helpTopics;

public ObservableCollection<HelpTopic> HelpTopics
{
get { return helpTopics; }
private set
{
if (helpTopics == value)
{
return;
}
helpTopics = value;
OnPropertyChanged("HelpTopics");
}
}

public PageViewModel()
{
HelpTopics = DataGenerator.GetHelpTopics();
}

protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{

handler.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}

The ViewModel only has one property [HelpTopics] because that’s currently the only one we need. Notice its private setter – we’re allowing setting this property from within the ViewModel only. Also, until we implement a better method to retrieve our data, we’re initializing the HelpTopics data collection directly in ViewModel’s constructor.

The ViewModel implements the INotifyPropertyChanged interface to notify its listeners when one of its properties has changed. Similar to what ObservableCollection does, but this one’s for a single class.

To finish this, all we have left is to compose a View. We’ll populate it with a TreeView control, set it to up to display our topics and create an instance of our PageViewModel for the DataContext:

<Grid x:Name="LayoutRoot">
    <Grid.DataContext>
        <local:PageViewModel />
    </Grid.DataContext>
    <slt:TreeView VerticalAlignment="Stretch" Margin="20" Width="300" ItemsSource="{Binding HelpTopics}">
        <slt:TreeView.ItemTemplate>
            <slt:HierarchicalDataTemplate ItemsSource="{Binding SubTopics}">
                <TextBlock Text="{Binding Name}" />
            </slt:HierarchicalDataTemplate>
        </slt:TreeView.ItemTemplate>
    </slt:TreeView>
</Grid>

The PageViewModel instance is set as a DataContext of the topmost page element (Grid), which means it will serve all page’s elements. One of its consumers is the TreeView control, which takes whatever is in its HelpTopics property for its items source. The last thing we had to do is set up a HierarchicalDataTemplate, specifying the property holding the subitems, and the visual of the tree item – a simple TextBlock with its Text property bound to the HelpTopic class’ Name property will suffice.

Help Topics TreeViewThis is what our TreeView control looks now. Far from being editable or anything yet, but except coding the dummy data generator and a class with one property, we hardly did any coding; And that’s the power of the MVVM pattern.

Next in the series: modifying the data structure.

The source code for this sample is available:

Tags:

Development | Development | Layout | Layout | Silverlight | Silverlight | User Experience | User Experience | MVVM | MVVM

Comments

1/22/2009 11:46:42 PM #

trackback

Trackback from Community Blogs

Silverlight Cream for January 22, 2009 - 4 -- #495

Community Blogs | Reply

1/23/2009 4:43:10 AM #

Max

Great, but can you bind SelectedItem to some ViewModel property?

Max Ukraine | Reply

1/23/2009 7:25:42 AM #

andrej

Max,
no you (currently) can't, but I'll get to (and over) this later in this series. Stay tuned.

andrej | Reply

1/23/2009 5:38:19 PM #

trackback

Trackback from Andrej Tozon's blog

Silverlight TreeView: MVVM and editing [3 – Delete]

Andrej Tozon's blog | Reply

1/25/2009 10:14:21 PM #

pingback

Pingback from silverlight-travel.com

Silverlight Travel » Silverlight TreeView: MVVM and editing

silverlight-travel.com | Reply

1/26/2009 4:25:29 PM #

trackback

Trackback from Andrej Tozon's blog

Silverlight TreeView: MVVM and editing [4 – Edit]

Andrej Tozon's blog | Reply

1/31/2009 3:11:07 PM #

volki

Hi Andrej
very helpful post! I still have a big problem I can not solve!
You wrote in your text:
'First, we’ll need a data generator that will create the data structure. In a real world scenario, you would call a web service to get such data, but we’ll get to that a bit later'
Do you have a sample that uses a ADO.NET Data Service to create a hierarchical treeview. I tried a lot but didn't get it to work so far. I tried to create the treeview with a table in SQL Server with the columns ID, ParentID, Name. But I can't get the values from the ADO.NET Data Service to the List that does the recursion. Do you have an idea how to solve this.
Thank you!
stefan

volki Switzerland | Reply

1/31/2009 6:03:37 PM #

andrej

Hi Stefan,
no, unfortunately I don't have an ADO.NET Data Service returning a hierarchical structure sample ready. I commonly call ordinary services to get all the data I need. I don't see why using ADO.NET DS wouldn't work, maybe using them would be a good exercise for the purpose of this series. I'll see what (when) I can do...

andrej | Reply

2/9/2009 2:39:22 AM #

Michael

Andrej/Stefan,
Regarding question about ADO.Net Data Service . I am also planning to use it , but, I guess, my problem is more generic. There is a need for typical load on demand scenario. One of the ways is to create a dummy node under parent and then in expanded event ( which needs to be wired for each TreeViewItem , not per TreeView , for some internal design reason for this control )  to call ADO.Net Data Service or other kind of service. It would be interesting to see how MVVM may be applied for that scenario. Level of nesting is arbitrary and how to model it using observable collection is not clear.

Michael United States | Reply

4/1/2009 4:14:30 AM #

trackback

Trackback from Dmitri Nesteruk's Blog

Working with SQL Server, hierarchical data and Silverlight

Dmitri Nesteruk's Blog | Reply

5/17/2009 9:53:46 AM #

trackback

Trackback from Dmitri Nesteruk's Blog

Working with SQL Server, hierarchical data and Silverlight

Dmitri Nesteruk's Blog | Reply

Add comment




  Country flag

biuquote
  • Comment
  • Preview
Loading





Andrej Tozon
Andrej Tozon's on Twitter View Andrej Tozon's profile on LinkedIn Subscribe to me on FriendFeed Andrej Tozon's Facebook profile

MVP - Client Application Developer

Microsoft Certified Solution Developer

MSN Alerts

Get help from Andrej Tozon!

 

Currently reading


Microsoft Silverlight 4 Data and Services Cookbook

Stay tuned for the review... In the mean time, you can read an excerpt from the book.

RecentComments

Comment RSS