Silverlight TreeView Control With CRUD and drag & drop

Sample download: https://1code.codeplex.com/releases/view/62253#DownloadId=215079

Amit Dey from Microsoft contributed a nice Silverlight code sample that demonstrates enabling CRUD (Create, Read, Update, Delete) and drag & drop in Silverlight TreeView control.  Silverlight TreeView control with CRUD and drag & drop is a frequently asked programming question in Silverlight  forums. Many customers also requested this code sample in our code sample request service. We hope that this sample can reduce developers' efforts in handling this typical programming scenario.

Thanks to Amit!

Silverlight TreeView Control With CRUD

https://deyamit.wordpress.com/2011/02/14/silverlight-treeview-control-with-crud/

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. This post assumes that you have atleast a nodding acquaintance with Silverlight and Data Binding. Our final output will look something like this.

Data

First lets 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 inorder to keep the UI in sync. Read this article to better understand this functionality.

Also notice the hepler functions Add and Delete which add and delete a child node respectively

 

/* File : Node.cs */
02 using System;
03 using System.ComponentModel;
04 using System.Collections.ObjectModel;
05   
06 public class Node : INotifyPropertyChanged
07 {
08     private String text;
09   
10     private ObservableCollection<Node> children;
11   
12     public event PropertyChangedEventHandler PropertyChanged;
13   
14     public ObservableCollection<Node> Children
15     {
16         get { return children; }
17         set { children = value; }
18     }
19   
20     public String Text
21     {
22         get { return text; }
23         set { text = value; }
24     }
25   
26     public Node(String text)
27     {
28         Children = new ObservableCollection<Node>();
29         Text = text;
30     }
31   
32     public void Add(Node node)
33     {
34         children.Add(node);
35         NotifyPropertyChanged("Children");
36     }
37   
38     public void Delete(Node node)
39     {
40         children.Remove(node);
41         NotifyPropertyChanged("Children");
42     }
43   
44     private void NotifyPropertyChanged(String info)
45     {
46          if (PropertyChanged != null)
47             PropertyChanged(this, new PropertyChangedEventArgs(info));
48     }
49 }

 

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.

01 <!-- File : CSSLTreeViewCrudDragDrop.xaml -->
02 <UserControl
03     xmlns:sdk="https://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
04     x:Class="CSSLTreeViewCRUDDragDrop.TreeViewCrudDragDrop"
05     xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
06     xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
07     xmlns:d="https://schemas.microsoft.com/expression/blend/2008"
08     xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006"
09     mc:Ignorable="d"
10     d:DesignHeight="300" d:DesignWidth="400"
11     xmlns:toolkit="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Toolkit"
12     xmlns:mswindows="clr-namespace:Microsoft.Windows;assembly=System.Windows.Controls.Toolkit">
13     <UserControl.Resources>
14         <!-- Template for Edit mode of TreeViewItem -->
15         <sdk:HierarchicalDataTemplate x:Key="TreeViewMainEditTemplate" ItemsSource="{Binding Children}">
16             <TextBox Text="{Binding Text,Mode=TwoWay}" >
17             </TextBox>
18         </sdk:HierarchicalDataTemplate>
19         <!-- Template for Read mode for TreeViewItem -->
20         <sdk:HierarchicalDataTemplate x:Key="TreeViewMainReadTemplate"
21                                       ItemsSource="{Binding Children}">
22             <TextBlock Text="{Binding Text,Mode=TwoWay}"
23                       MouseRightButtonDown="TreeViewMain_MouseRightButtonDown"
24                       MouseRightButtonUp="TreeViewMain_MouseRightButtonUp"
25                       MouseLeftButtonDown="TreeViewMain_MouseLeftButtonDown" >
26             </TextBlock>
27         </sdk:HierarchicalDataTemplate>
28     </UserControl.Resources>
29     <Grid x:Name="LayoutRoot" Background="White">
30         <!-- TreeViewDragDropTarget from Toolkit to add DragAndDrop feature -->
31         <toolkit:TreeViewDragDropTarget AllowDrop="True">
32             <!-- Custom TreeView -->
33             <sdk:TreeView Name="TreeViewMain"
34                       ItemTemplate="{StaticResource TreeViewMainReadTemplate}"
35                       MouseRightButtonDown="TreeViewMain_MouseRightButtonDown"
36                       MouseRightButtonUp="TreeViewMain_MouseRightButtonUp"
37                       MouseLeftButtonDown="TreeViewMain_MouseLeftButtonDown"
38                       Width="400" Height="400"  >
39             </sdk:TreeView>
40         </toolkit:TreeViewDragDropTarget>
41         <!-- Context Menu -->
42         <Canvas>
43             <Popup Name="ContextMenu" Visibility="Collapsed">
44                 <Border BorderThickness="1" BorderBrush="Black" Background="White">
45                     <StackPanel>
46                         <HyperlinkButton Content="Add" Name="AddButton" Click="AddButton_Click" />
47                         <HyperlinkButton Content="Edit" Name="EditButton" Click="EditButton_Click"/>
48                         <HyperlinkButton Content="Delete" Name="DeleteButton" Click="DeleteButton_Click"/>
49                     </StackPanel>
50                 </Border>
51             </Popup>
52         </Canvas>
53     </Grid>
54 </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.

001 /* File : CSSLTreeViewCrudDragDrop.cs */
002 using System;
003 using System.Collections.Generic;
004 using System.Linq;
005 using System.Windows;
006 using System.Windows.Controls;
007 using System.Windows.Input;
008 using System.Collections.ObjectModel;
009   
010 namespace CSSLTreeViewCRUDDragDrop
011 {
012   
013     public partial class TreeViewCrudDragDrop : UserControl
014     {
015         ObservableCollection<Node> objectTree;
016   
017         Node selectedNode;
018   
019         public List<Node> Items
020         {
021             get
022             {
023                 return objectTree.ToList<Node>();
024             }
025             set
026             {
027                 objectTree = new ObservableCollection<Node>(value);
028                 TreeViewMain.ItemsSource = objectTree;
029             }
030         }
031   
032         public TreeViewCrudDragDrop()
033         {
034             InitializeComponent();
035             objectTree = new ObservableCollection<Node>();
036             TreeViewMain.ItemsSource = objectTree;
037         }
038   
039         private void TreeViewMain_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
040         {
041             DisableEditForSelectedItem();
042   
043             e.Handled = true;
044         }
045   
046         private void TreeViewMain_MouseRightButtonUp(object sender, MouseButtonEventArgs e)
047         {
048             DisableEditForSelectedItem();
049   
050             if (sender is TextBlock)
051             {
052                 selectedNode = (Node)((sender as TextBlock).DataContext);
053             }
054             else
055             {
056                 selectedNode = null;
057             }
058   
059             ShowContextMenu(e);
060         }
061   
062         private void TreeViewMain_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
063         {
064             DisableEditForSelectedItem();
065   
066             HideContextMenu();
067         }
068   
069         private void AddButton_Click(object sender, RoutedEventArgs e)
070         {
071             Node newNode = new Node("New Node");
072   
073             if (selectedNode != null)
074             {
075                 selectedNode.Add(newNode);
076             }
077             else
078             {
079                 if (objectTree != null)
080                 {
081                     objectTree.Add(newNode);
082                 }
083                 else
084                 {
085                     objectTree = new ObservableCollection<Node>();
086                     objectTree.Add(newNode);
087                 }
088             }
089   
090             HideContextMenu();
091         }
092   
093         private void EditButton_Click(object sender, RoutedEventArgs e)
094         {
095             EnalbleEditForSelectedItem();
096   
097             TreeViewItem selectedTreeViewItem =
098                 TreeViewExtensions.GetContainerFromItem(TreeViewMain, selectedNode);
099   
100             HideContextMenu();
101         }
102   
103         private void DeleteButton_Click(object sender, RoutedEventArgs e)
104         {
105             TreeViewItem selectedTreeViewItem =
106                 TreeViewExtensions.GetContainerFromItem(TreeViewMain, selectedNode);
107   
108             if (selectedTreeViewItem != null)
109             {
110                 TreeViewItem selectedTreeViewItemParent =
111                     TreeViewExtensions.GetParentTreeViewItem(selectedTreeViewItem);
112   
113                 if (selectedTreeViewItemParent != null)
114                 {
115                     Node seleactedParentNode = (Node)selectedTreeViewItemParent.DataContext;
116                     seleactedParentNode.Delete(selectedNode);
117                 }
118                 else
119                 {
120                     objectTree.Remove(selectedNode);
121                 }
122             }
123   
124             HideContextMenu();
125         }
126   
127         private void ShowContextMenu(MouseButtonEventArgs e)
128         {
129             e.Handled = true;
130             Point p = e.GetPosition(this);
131             ContextMenu.Visibility = Visibility.Visible;
132             ContextMenu.IsOpen = true;
133             ContextMenu.SetValue(Canvas.LeftProperty, (double)p.X);
134             ContextMenu.SetValue(Canvas.TopProperty, (double)p.Y);
135         }
136   
137         private void HideContextMenu()
138         {
139             ContextMenu.Visibility = Visibility.Collapsed;
140             ContextMenu.IsOpen = false;
141         }
142   
143         private void EnalbleEditForSelectedItem()
144         {
145             if (selectedNode != null)
146             {
147                 SetTemplateForSelectedItem("TreeViewMainEditTemplate");
148             }
149         }
150   
151         private void DisableEditForSelectedItem()
152         {
153             if (selectedNode != null)
154             {
155                 SetTemplateForSelectedItem("TreeViewMainReadTemplate");
156                 selectedNode = null;
157             }
158         }
159   
160         private void SetTemplateForSelectedItem(String templateName)
161         {
162             HierarchicalDataTemplate hdt = (HierarchicalDataTemplate)Resources[templateName];
163   
164             TreeViewItem selectedTreeViewItem =
165                 TreeViewExtensions.GetContainerFromItem(TreeViewMain, selectedNode);
166   
167             if (selectedTreeViewItem != null)
168                 selectedTreeViewItem.HeaderTemplate = hdt;
169         }
170     }
171 }

So that completes it.

References

MichaelSnow : Silverlight Tip of the Day #3 – Mouse Right Clicks
MSDN : DataBinding Silverlight
Codeplex : Silverlight Toolkit
MSDN : INotifyPropertyChanged Interface