Click here to Skip to main content
Licence CPOL
First Posted 15 Feb 2011
Views 14,286
Downloads 0
Bookmarked 22 times

Silverlight TreeView with Crud

By | 26 Mar 2012 | Article
A Silverlight TreeView control with Crud operations

Introduction

This is a code sample for a Silverlight TreeView Control which supports CRUD (Create, Read, Update, Delete) operations. In addition, it supports Drag & Drop of items. Our final output will look something like this:

SilverlightTreeViewCrud/treeviewlook.png

Background

This post assumes that you have atleast a nodding acquaintance with Silverlight and Data Binding.

Using the Code

Data

First let us have a look at the data structure which is bound to the TreeView control.
Node is the class, whose instance is bound to each TreeViewItem.
Text represents the data at a node.
Children represents the childs of a node.
Notice that the Node inherits System.ComponentModel.INotifyPropertyChanged class in order to keep the UI in sync. Read this article to better understand this functionality. Also notice the helper functions Add and Delete which add and delete a child node respectively.

/* File : Node.cs */
using System;
using System.ComponentModel;
using System.Collections.ObjectModel;

public class Node : INotifyPropertyChanged
{
    private String text;

    private ObservableCollection<node> children;

    public event PropertyChangedEventHandler PropertyChanged;

    public ObservableCollection<node> Children
    {
        get { return children; }
        set { children = value; }
    }

    public String Text
    {
        get { return text; }
        set { text = value; }
    }

    public Node(String text)
    {
        Children = new ObservableCollection<node />();
        Text = text;
    }

    public void Add(Node node)
    {
        children.Add(node);
        NotifyPropertyChanged("Children");
    }

    public void Delete(Node node)
    {
        children.Remove(node);
        NotifyPropertyChanged("Children");
    }

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

UserControl XAML

Now let us see the XAML definition for the user control.
First thing, I have implemented a Context Menu to facilitate CRUD operations. You can read this blog to learn how one can be implemented.
Next, notice the two HierarchicalDataTemplate. One is for the TreeViewItem in Read Mode (hence a TextBlock) and the other in Edit mode (hence a TextBox). The TextBox and the TextBlock are bound to the Text property of the Node.
I am using the TreeViewDragDropTarget control from Silverlight Toolkit to enable Drag-And-Drop of TreeViewItems among parent nodes.

<!-- File : CSSLTreeViewCrudDragDrop.xaml -->
<UserControl
    xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
    x:Class="CSSLTreeViewCRUDDragDrop.TreeViewCrudDragDrop"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008>"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400"
    xmlns:toolkit="clr-namespace:System.Windows.Controls;
	assembly=System.Windows.Controls.Toolkit"
    xmlns:mswindows="clr-namespace:Microsoft.Windows;
	assembly=System.Windows.Controls.Toolkit">
    <UserControl.Resources>
        <!-- Template for Edit mode of TreeViewItem -->
        <sdk:HierarchicalDataTemplate x:Key="TreeViewMainEditTemplate" 
		ItemsSource="{Binding Children}">
            <TextBox Text="{Binding Text,Mode=TwoWay}" >
            </TextBox>
        </sdk:HierarchicalDataTemplate>
        <!-- Template for Read mode for TreeViewItem -->
        <sdk:HierarchicalDataTemplate x:Key="TreeViewMainReadTemplate"
                                      ItemsSource="{Binding Children}">
            <TextBlock Text="{Binding Text,Mode=TwoWay}"
                      MouseRightButtonDown="TreeViewMain_MouseRightButtonDown"
                      MouseRightButtonUp="TreeViewMain_MouseRightButtonUp"
                      MouseLeftButtonDown="TreeViewMain_MouseLeftButtonDown" >
            </TextBlock>
        </sdk:HierarchicalDataTemplate>
    </UserControl.Resources>
    <Grid x:Name="LayoutRoot" Background="White">
        <!-- TreeViewDragDropTarget from Toolkit to add DragAndDrop feature -->
        <toolkit:TreeViewDragDropTarget AllowDrop="True">
            <!-- Custom TreeView  -->
            <sdk:TreeView Name="TreeViewMain"
                      ItemTemplate="{StaticResource TreeViewMainReadTemplate}"
                      MouseRightButtonDown="TreeViewMain_MouseRightButtonDown"
                      MouseRightButtonUp="TreeViewMain_MouseRightButtonUp"
                      MouseLeftButtonDown="TreeViewMain_MouseLeftButtonDown"
                      Width="400" Height="400"  >
            </sdk:TreeView>
        </toolkit:TreeViewDragDropTarget>
        <!-- Context Menu -->
        <Canvas>
            <Popup Name="ContextMenu" Visibility="Collapsed">
                <Border BorderThickness="1" BorderBrush="Black" Background="White">
                    <StackPanel>
                        <HyperlinkButton Content="Add" Name="AddButton" 
			Click="AddButton_Click" />
                        <HyperlinkButton Content="Edit" Name="EditButton" 
			Click="EditButton_Click"/>
                        <HyperlinkButton Content="Delete" Name="DeleteButton" 
			Click="DeleteButton_Click"/>
                    </StackPanel>
                </Border>
            </Popup>
        </Canvas>
    </Grid>
</UserControl>

Code Behind

Now let us have a sneak peek at the code behind for our UserControl.
First, the Mouse Event Handlers. The MouseRightButtonUp event for a TreeViewItem does two things. It assigns that particular TreeViewItem’s Data Context as the selectedNode. Second, it shows up the ContextMenu. The selectedNode information is necessary as that is used as a reference to Edit the TreeViewItem. Add Children to the TreeViewItem or delete the TreeViewItem. The AddButton_Click event handler, creates a new Node and adds it as a children of the selecteNode. The EditButton_Click event handler, changes the Template of the selected TreeViewItem to Edit mode.
The DeleteButton_Click event handler first identifies the TreeViewItem associated with the selectedNode, finds its parent, and deletes the selectedNode from the Parent.

/* File : CSSLTreeViewCrudDragDrop.cs */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Collections.ObjectModel;
namespace CSSLTreeViewCRUDDragDrop
{
    public partial class TreeViewCrudDragDrop : UserControl
    {
        ObservableCollection<Node> objectTree;
        Node selectedNode;
        public List<Node> Items
        {
            get
            {
                return objectTree.ToList<Node>();
            }
            set
            {
                objectTree = new ObservableCollection<Node>(value);
                TreeViewMain.ItemsSource = objectTree;
            }
        }
        public TreeViewCrudDragDrop()
        {
            InitializeComponent();
            objectTree = new ObservableCollection<Node>();
            TreeViewMain.ItemsSource = objectTree;
        }
        private void TreeViewMain_MouseRightButtonDown
		(object sender, MouseButtonEventArgs e)
        {
            DisableEditForSelectedItem();
            e.Handled = true;
        }
        private void TreeViewMain_MouseRightButtonUp(object sender, MouseButtonEventArgs e)
        {
            DisableEditForSelectedItem();
            if (sender is TextBlock)
            {
                selectedNode = (Node)((sender as TextBlock).DataContext);
            }
            else
            {
                selectedNode = null;
            }
            ShowContextMenu(e);
        }
        private void TreeViewMain_MouseLeftButtonDown
		(object sender, MouseButtonEventArgs e)
        {
            DisableEditForSelectedItem();
            HideContextMenu();
        }
        private void AddButton_Click(object sender, RoutedEventArgs e)
        {
            Node newNode = new Node("New Node");
            if (selectedNode != null)
            {
                selectedNode.Add(newNode);
            }
            else
            {
                if (objectTree != null)
                {
                    objectTree.Add(newNode);
                }
                else
                {
                    objectTree = new ObservableCollection<Node>();
                    objectTree.Add(newNode);
                }
            }
            HideContextMenu();
        }
        private void EditButton_Click(object sender, RoutedEventArgs e)
        {
            EnalbleEditForSelectedItem();
            TreeViewItem selectedTreeViewItem =
                TreeViewExtensions.GetContainerFromItem(TreeViewMain, selectedNode);
            HideContextMenu();
        }
        private void DeleteButton_Click(object sender, RoutedEventArgs e)
        {
            TreeViewItem selectedTreeViewItem =
                TreeViewExtensions.GetContainerFromItem(TreeViewMain, selectedNode);
            if (selectedTreeViewItem != null)
            {
                TreeViewItem selectedTreeViewItemParent =
                    TreeViewExtensions.GetParentTreeViewItem(selectedTreeViewItem);
                if (selectedTreeViewItemParent != null)
                {
                    Node seleactedParentNode = 
			(Node)selectedTreeViewItemParent.DataContext;
                    seleactedParentNode.Delete(selectedNode);
                }
                else
                {
                    objectTree.Remove(selectedNode);
                }
            }
            HideContextMenu();
        }
        private void ShowContextMenu(MouseButtonEventArgs e)
        {
            e.Handled = true;
            Point p = e.GetPosition(this);
            ContextMenu.Visibility = Visibility.Visible;
            ContextMenu.IsOpen = true;
            ContextMenu.SetValue(Canvas.LeftProperty, (double)p.X);
            ContextMenu.SetValue(Canvas.TopProperty, (double)p.Y);
        }
        private void HideContextMenu()
        {
            ContextMenu.Visibility = Visibility.Collapsed;
            ContextMenu.IsOpen = false;
        }
        private void EnalbleEditForSelectedItem()
        {
            if (selectedNode != null)
            {
                SetTemplateForSelectedItem("TreeViewMainEditTemplate");
            }
        }
        private void DisableEditForSelectedItem()
        {
            if (selectedNode != null)
            {
                SetTemplateForSelectedItem("TreeViewMainReadTemplate");
                selectedNode = null;
            }
        }
        private void SetTemplateForSelectedItem(String templateName)
        {
            HierarchicalDataTemplate hdt = 
		(HierarchicalDataTemplate)Resources[templateName];
            TreeViewItem selectedTreeViewItem =
                TreeViewExtensions.GetContainerFromItem(TreeViewMain, selectedNode);
            if (selectedTreeViewItem != null)
                selectedTreeViewItem.HeaderTemplate = hdt;
        }
    }
}

So that completes it.

References

History

  • Created article on 16 Feb 2011
  • Added source code on 3 March 2011

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

AmitDey

Software Developer
Microsoft India
India India

Member

Follow on Twitter Follow on Twitter
My Name is Amit Dey.
I work in Microsoft as a Software Developer for the last 3 Years.
 
My MSDN Blog
 
My Windows Phone Apps

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board. (secure sign-in)
 
Search this forum  
 FAQ
    Noise  Layout  Per page   
  Refresh
QuestionDownload Zip is corrupt PinmemberDarren H12:26 21 Mar '12  
AnswerRe: Download Zip is corrupt PinmemberAmitDey22:00 25 Mar '12  
GeneralCode Link is Broken PinmemberDewey20:39 22 Mar '11  
GeneralRe: Code Link is Broken PinmemberAmitDey20:41 22 Mar '11  
GeneralRe: Code Link is Broken PinmemberDewey8:46 23 Mar '11  
General3rd article - connection to a database Pinmemberjeffb428:26 26 Feb '11  
GeneralRe: 3rd article - connection to a database PinmemberAmitDey20:20 9 Mar '11  
GeneralYou should try to present this using MVVM pattern PinmemberZebZak7:59 18 Feb '11  
GeneralRe: You should try to present this using MVVM pattern PinmemberAmitDey8:23 18 Feb '11  
GeneralRe: You should try to present this using MVVM pattern Pinmemberjeffb428:01 26 Feb '11  

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Mobile
Web01 | 2.5.120517.1 | Last Updated 26 Mar 2012
Article Copyright 2011 by AmitDey
Everything else Copyright © CodeProject, 1999-2012
Terms of Use
Layout: fixed | fluid