带 CRUD 操作的 Silverlight TreeView
带有 CRUD 操作的 Silverlight TreeView 控件
引言
这是一个 Silverlight TreeView
控件的代码示例,支持 CRUD(创建、读取、更新、删除)操作。此外,它还支持项目拖放。我们的最终输出将如下所示

背景
本文假定您至少对 Silverlight 和数据绑定有所了解。
Using the Code
Data
首先,让我们看一下绑定到 TreeView
控件的数据结构。Node
是类,其实例绑定到每个 TreeViewItem
。Text
表示节点中的数据。Children
表示节点的子节点。
请注意,Node 继承了 System.ComponentModel.INotifyPropertyChanged
类,以便保持 UI 同步。阅读 这篇文章 以更好地理解此功能。 另外请注意辅助函数 Add
和 Delete
,它们分别添加和删除子节点。
/* 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
现在让我们看一下用户控件的 XAML 定义。
首先,我实现了一个上下文菜单来方便 CRUD 操作。您可以阅读 这篇博客 以了解如何实现它。
接下来,请注意两个 HierarchicalDataTemplate
。一个用于读取模式下的 TreeViewItem
(因此使用 TextBlock
),另一个用于编辑模式(因此使用 TextBox
)。 TextBox
和 TextBlock
绑定到 Node 的 Text
属性。
我正在使用 Silverlight Toolkit 中的 TreeViewDragDropTarget
控件来启用 TreeViewItems
在父节点之间的拖放。
<!-- 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>
代码后置
现在让我们快速看一下我们的 UserControl
的代码。
首先,是鼠标事件处理程序。 TreeViewItem
的 MouseRightButtonUp
事件执行两件事。它将该特定 TreeViewItem
的数据上下文分配为 selectedNode
。其次,它显示 ContextMenu
。 selectedNode
信息是必要的,因为它用作编辑 TreeViewItem
的引用。 向 TreeViewItem
添加子项或删除 TreeViewItem
。 AddButton_Click
事件处理程序创建一个新的 Node
并将其作为 selecteNode
的子项添加。 EditButton_Click
事件处理程序将所选 TreeViewItem
的模板更改为编辑模式。DeleteButton_Click
事件处理程序首先识别与 selectedNode
关联的 TreeViewItem
,找到其父项,然后从父项中删除 selectedNode
。
/* 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;
}
}
}
就这样完成了。
参考文献
- MichaelSnow:Silverlight Tip of the Day #3 – Mouse Right Clicks
- MSDN:Silverlight 数据绑定
- Codeplex:Silverlight Toolkit
- MSDN:INotifyPropertyChanged 接口
历史
- 创建文章日期:2011 年 2 月 16 日
- 添加源代码日期:2011 年 3 月 3 日