XAML TreeView with Context Menu, Drag and Drop features

The C# code sample below demonstrates how to work with the TreeView in XAML.

1) TreeView Context Menu,
2) TreeView to allow Rearrange the nodes within the same level and Drag/Drop in sibling levels,
3) Drag Drop the nodes from TreeView to DataGrid and vice versa.

Note : Please refer the attached Zip folder which consists of sample demo silverlight application along with document which explains above features with screenshots.

1) TreeView Context Menu:
If you want to add new node or rename the existing node or delete the node in Treeview, you can add the Context Menu feature to TreeView with Add, Rename and Delete menu options
Steps to implement the Context Menu for TreeView.

a) Add xml name space to the XAML file

“xmlns:sdk=http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk

b) Add TreeView Control and its Binding Properties to XAML

<sdk:TreeView x:Name="treeViewEmployee" BorderThickness="0" HorizontalAlignment="Left" ItemContainerStyle="{StaticResource treeViewItemStyle}" ItemsSource="{Binding EmployeeCollection}" ItemTemplate="{StaticResource TreeViewMainReadTemplate}"/>

c) Define the HierarcalDataTemplate and add Context Menu as below.
<!-- Template for Edit mode of TreeViewItem -->
<sdk:HierarchicalDataTemplate x:Key="TreeViewMainEditTemplate"
ItemsSource="{Binding ChildCollection}">
<TextBox Text="{Binding Name, Mode=TwoWay}" Width="100" LostFocus="Name_LostFocus" />
</sdk:HierarchicalDataTemplate>

<!-- Template for Read mode of TreeViewItem -->
<sdk:HierarchicalDataTemplate ItemsSource="{Binding ChildCollection}" x:Key="TreeViewMainReadTemplate">
<StackPanel>
<TextBlock Text="{Binding Name}">
<toolkit:ContextMenuService.ContextMenu>
<toolkit:ContextMenu >
<toolkit:MenuItem Header="Add" Click="MenuItem_Click_Add"/>
<toolkit:MenuItem Header="Rename" Click="MenuItem_Click_Rename"/>
<toolkit:MenuItem Header="Delete" Click="MenuItem_Click_Delete"/>
</toolkit:ContextMenu>
</toolkit:ContextMenuService.ContextMenu>
</TextBlock>
</StackPanel>
</sdk:HierarchicalDataTemplate>

d) Add the DataContext in XAML or in XAML.CS.

this.DataContext = viewModel;

e) Add the Events for Context Menu, in this case we have 3 menus, so, I am attaching Click event individually which will create Event Handler method in XAML.CS file as below.
private void MenuItem_Click_Add(object sender, RoutedEventArgs e)
{
if (this.treeViewEmployee.SelectedItem != null)
{
TreeViewItem selectedTreeViewItem =
TreeViewExtensions.GetContainerFromItem(this.treeViewEmployee, this.treeViewEmployee.SelectedItem);
if (selectedTreeViewItem != null)
{
selectedTreeViewItem.IsExpanded = true;
}

this.selectedEmployee = this.treeViewEmployee.SelectedItem as Employee;

if (this.selectedEmployee.ChildCollection == null)
{
this.selectedEmployee.ChildCollection = new ObservableCollection<Employee>();
}

Employee addItem = new Employee();
addItem.Name = "";
addItem.Level = this.selectedEmployee.Level + 1;
addItem.IsSelected = true;
this.selectedEmployee.ChildCollection.Add(addItem);

this.EnalbleEditForSelectedItem(addItem);}

private void MenuItem_Click_Rename(object sender, RoutedEventArgs e)
{
this.EnalbleEditForSelectedItem(this.treeViewEmployee.SelectedItem as Employee);}

private void MenuItem_Click_Delete(object sender, RoutedEventArgs e)
{

TreeViewItem selectedTreeViewItem =
TreeViewExtensions.GetContainerFromItem(this.treeViewEmployee, this.treeViewEmployee.SelectedItem);

if (selectedTreeViewItem != null)
{
TreeViewItem selectedTreeViewItemParent =
TreeViewExtensions.GetParentTreeViewItem(selectedTreeViewItem);

if (selectedTreeViewItemParent != null)
{
Employee seleactedParentNode = (Employee)selectedTreeViewItemParent.DataContext;
Employee removeItem = (Employee)this.treeViewEmployee.SelectedItem;
seleactedParentNode.ChildCollection.Remove(removeItem);
}
}
}
private void SetTemplateForSelectedItem(String templateName, Employee selectedNode)
{
HierarchicalDataTemplate hdt = (HierarchicalDataTemplate)Resources[templateName];

TreeViewItem selectedTreeViewItem = TreeViewExtensions.GetContainerFromItem(this.treeViewEmployee, selectedNode);


if (selectedTreeViewItem != null)
{
selectedTreeViewItem.HeaderTemplate = hdt;
}
else
{
if (this.selectedEmployee != null)
{
if (this.selectedEmployee.ChildCollection != null && this.selectedEmployee.ChildCollection.Contains(selectedNode))
{
this.selectedEmployee.ChildCollection.Remove(selectedNode);
}
}
}
}

private void DisableEditForSelectedItem(Employee selectedNode)
{
if (selectedNode != null)
{
this.SetTemplateForSelectedItem("TreeViewMainReadTemplate", selectedNode);
selectedNode = null;
}
}

private void EnalbleEditForSelectedItem(Employee selectedNode)
{
if (selectedNode != null)
{
this.SetTemplateForSelectedItem("TreeViewMainEditTemplate", selectedNode);
}
}

private void Name_LostFocus(object sender, RoutedEventArgs e)
{
if (!string.IsNullOrEmpty(((TextBox)sender).Text))
{
((Employee)(this.treeViewEmployee.SelectedItem)).Name = ((TextBox)sender).Text;
this.DisableEditForSelectedItem(this.treeViewEmployee.SelectedItem as Employee);
}
}
Usage:

Follow the below usage steps to Add/Rename/Delete the Node.

1) Select and right click on the Node, Click on Add Menu then new node will be added with empty TextBox where user can enter.
2) Enter the required node name and Tab out the changes to be effected.
3) Similarly, you can add nodes to any level
4) Select and right click on the Node, Clicks on Rename Menu, then Rename the Node and tab out to reflect the changes.
5) Select and right click on the Node, clicks in Delete Menu then that node will be removed from the TreeView

2) TreeView to allow Rearrange the nodes within the same level and Drag/Drop in sibling levels:

If you wish to rearrange the nodes within the same level and drag and drop the child nodes in another parent level, you can enable the Drag and Drop feature to TreeView. Based on the requirement, you can also restrict the Child node to be dropped as Parent Node by dynamically disable the AllowDrop property of TreeView.

Follow the below steps to implement this feature.
a) Add the xml name space to the XAML file

xmlns:toolkit=http://schemas.microsoft.com/winfx/2006/xaml/presentation/toolkit

b) Use “TreeViewDragDropTarget” control and define the TreeView inside this control as below.

<toolkit:TreeViewDragDropTarget Margin="5" x:Name="treeviewDragDrop" AllowDrop="True" DragOver="TreeViewDragDropTarget_DragOver" ItemDragStarting="treeviewDragDrop_ItemDragStarting" >
<sdk:TreeView Name="treeViewEmployee" BorderThickness="0" HorizontalAlignment="Left" ItemContainerStyle="{StaticResource treeViewItemStyle}" ItemsSource="{Binding EmployeeCollection}">
<sdk:TreeView.ItemTemplate>
<sdk:HierarchicalDataTemplate ItemsSource="{Binding ChildCollection}">
<StackPanel>
<TextBlock Text="{Binding Name}">
</TextBlock>
</StackPanel>
</sdk:HierarchicalDataTemplate>
</sdk:TreeView.ItemTemplate>
</sdk:TreeView>
</toolkit:TreeViewDragDropTarget>

c) Add the DragOver and ItemDragStarting events to “TreeViewDragDropTarget”.
private void TreeViewDragDropTarget_DragOver(object sender, Microsoft.Windows.DragEventArgs e)
{
Employee selectedItem = this.treeViewEmployee.SelectedItem as Employee;
TreeViewItem treeViewItem = FindAncestor<TreeViewItem>((DependencyObject)e.OriginalSource);
if (treeViewItem != null && selectedItem != null)
{
Employee droppedItem = (Employee)treeViewItem.Header;

if (droppedItem != null && droppedItem.Level != selectedItem.Level)
{
this.treeViewEmployee.AllowDrop = false;
e.Effects = DragDropEffects.None;
}
else
{
this.treeViewEmployee.AllowDrop = true;
this.dragDropEffects = e.Effects;
}
}
else
{
this.dragDropEffects = DragDropEffects.None;
this.treeViewEmployee.AllowDrop = false;
}
}

private static T FindAncestor<T>(DependencyObject current) where T : DependencyObject
{ // Search the VisualTree for specified type
while (current != null)
{
if (current is T)
{
return (T)current;
}
current = VisualTreeHelper.GetParent(current);
}
return null;
}

d) In the above method, I am enabling/disabling the” AllowDrop” property for TreeView based on the Level number.
For example, you want to allow the Drag and Drop in sibling levels means within the same level the nodes can be dragged and dropped ( a child node cannot be a Parent and vice versa).  Based on the Level Number you can enable/disable the “AllowDrop” Property as described in the above DragOver Event .

private void treeviewDragDrop_ItemDragStarting(object sender, ItemDragEventArgs e)
{
this.treeViewEmployee.ReleaseMouseCapture();
this.treeviewDragDrop.ReleaseMouseCapture();
this.treeViewEmployee.AllowDrop = true;
this.treeviewDragDrop.AllowDrop = true;
e.Effects = DragDropEffects.All;
}

e) In the above “ItemDragStarting” event , just enabling the “AllowDrop” Property for TreeView which will be executed when you start dragging a node.

Usage:
1) Rearrange the nodes within the same level. The arrow indicator points the Preview line which will be used to indicate the position of the node while dragging and dropping.
2)The child node in one parent can be dragged and dropped to another parent as child node.
3)Disable the Dop when you try to drag the child item as Parent node.

3) Drag Drop the nodes from TreeView to DataGrid:

If you want drag and Drop the items from TreeView to Datagrid or from Datagrid to TreeView, you can implement based on below mechanism.

For enabling the Drag and Drop for TreeView, use “TreeViewDragDropTarget” as discussed in above feature.
Similarly, for enabling the Drag and Drop For Datagrid, use “DataGridDragDropTarget”.

a) Add below xml namespaces to XAML file

xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
xmlns:toolkit="http://schemas.microsoft.com/winfx/2006/xaml/presentation/toolkit" xmlns:controlsToolkit="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Toolkit"

b) Add TreeView and DataGrid Controls under their respective DragDropTarget Controls as below
<toolkit:TreeViewDragDropTarget Margin="5" x:Name="treeviewDragDrop" AllowDrop="True" >
<sdk:TreeView Name="treeViewEmployee" BorderThickness="0" ItemContainerStyle="{StaticResource treeViewItemStyle}" HorizontalAlignment="Left"
ItemsSource="{Binding EmployeeCollection}">
<sdk:TreeView.ItemTemplate>
<sdk:HierarchicalDataTemplate ItemsSource="{Binding ChildCollection}" >
<StackPanel>
<TextBlock Text="{Binding Name}">
</TextBlock>
</StackPanel>
</sdk:HierarchicalDataTemplate>
</sdk:TreeView.ItemTemplate>
</sdk:TreeView>
</toolkit:TreeViewDragDropTarget>
<toolkit:DataGridDragDropTarget Name="datagridDragDrop" AllowDrop="True" Drop="datagridDragDrop_Drop" >
<sdk:DataGrid Name="datagrid1" AutoGenerateColumns="False" ItemsSource="{Binding GridCollection}" Margin="5" VerticalAlignment="Top" Width="300"
Height="200" >
<sdk:DataGrid.Columns>
<sdk:DataGridTextColumn Binding="{Binding Name}" Header="Name" Width="200" IsReadOnly="True"></sdk:DataGridTextColumn>
</sdk:DataGrid.Columns>
</sdk:DataGrid>

</toolkit:DataGridDragDropTarget>

c) If you drag the Node from Treeview to DataGrid and if that Node has child Items, those items should also be added to the DataGrid. In this case, you can add the Drop Event to DataGrid and find the SelectedNode from TreeView then loop through the child items and then add them to the DataGrid as described in below.

private void datagridDragDrop_Drop(object sender, Microsoft.Windows.DragEventArgs e)
{
Employee selectedEmployee = this.treeViewEmployee.SelectedItem as Employee;

if (selectedEmployee != null && selectedEmployee.ChildCollection != null)
{
foreach (var employee in selectedEmployee.ChildCollection)
{
viewModel.GridCollection.Add(employee);
}
}
}

Please use this url for downloading the sample application and document with screenshots
Download

By Siva Jagan Dhulipalla   Popularity  (1884 Views)