启用数据绑定的分层 TreeView 控件






4.46/5 (15投票s)
该控件扩展了标准TreeView控件,使其完全支持数据绑定。
引言
前段时间,我的任务是编写类似虚拟文件系统的东西。当然,我决定使用类型化的DataSet
,因为我已经编写了一个可以轻松工作和更新它们的框架。 使用这项技术,很容易显示文件夹的内容。 关系型DataTable
是非常棒的工具。 一切都很好,但是当我看到结果时 - 我崩溃了! 这不是我告诉客户的外观和感觉! 所以我打开Google并开始搜索启用数据绑定的TreeView
。 当然,我找到了一些东西:这个(C#中的数据绑定TreeView)很漂亮,但那不是我脑海中的层次结构;这个(如何使用基类和数据提供程序将分层数据填充到TreeView中)也很漂亮,但我不明白为什么作者不喜欢标准绑定。 两者都不适合我! 作为一个真正的乌克兰人,我决定自己写一个!
绑定实现
首先,我们需要一种方法来填充树视图中的所有数据。为此,我将数据项的引用存储在ArrayList
中。这给了我一个不在树中的项目的指示器。我将迭代这些项目,直到此数组的长度变为0
。如果某些项目无法在try中找到它们的位置,则此实现将抛出异常。如果您需要此方法的另一种实现(例如,不执行任何操作或将这些项目存储在根节点的底部),请告诉我。我会尝试更新这篇文章。
ArrayList unsortedNodes = new ArrayList();
//This is list of items that still have no place in tree
for (int i = 0; i < this.listManager.Count; i++)
{
//Fill this list with all items.
unsortedNodes.Add(this.CreateNode(this.listManager, i));
}
int startCount;
//Iterate until list will not empty.
while (unsortedNodes.Count > 0)
{
startCount = unsortedNodes.Count;
for (int i = unsortedNodes.Count-1; i >= 0 ; i--)
{
if (this.TryAddNode((DataTreeViewNode)unsortedNodes[i]))
{
//Item found its place.
unsortedNodes.RemoveAt(i);
}
}
if (startCount == unsortedNodes.Count)
{
//Throw if nothing was done, in another way this
//will continuous loop.
throw new ApplicationException("Tree view confused
when try to make your data hierarchical.");
}
}
private bool TryAddNode(DataTreeViewNode node)
{
if (this.IsIDNull(node.ParentID))
{
//If parent is null this mean that this is root node.
this.AddNode(this.Nodes, node);
return true;
}
else
{
if (this.items_Identifiers.ContainsKey(node.ParentID))
{
//Parent already exists in tree so we can add item to it.
TreeNode parentNode =
this.items_Identifiers[node.ParentID] as TreeNode;
if (parentNode != null)
{
this.AddNode(parentNode.Nodes, node);
return true;
}
}
}
//Parent was not found at this point.
return false;
}
响应外部数据更改
好的……现在我们的树视图已填充了所有项目。我们需要做的第二件事是响应外部数据更改。为此,我们需要处理当前上下文的ListChanged
事件。
((IBindingList)this.listManager.List).ListChanged +=
new ListChangedEventHandler(DataTreeView_ListChanged);
句柄的实现非常简单。
private void DataTreeView_ListChanged(object sender, ListChangedEventArgs e)
{
switch(e.ListChangedType)
{
case ListChangedType.ItemAdded:
//Add item here.
break;
case ListChangedType.ItemChanged:
//Change node associated with this item
break;
case ListChangedType.ItemMoved:
//Parent changed.
break;
case ListChangedType.ItemDeleted:
//Item removed
break;
case ListChangedType.Reset:
//This reset all data and control need to refill all data.
break;
}
}
现在我们的控件特别支持数据绑定。您能够看到数据,它将与外部数据同步更改。
选定的节点
您可以问需要什么其他功能? - 哦!这仅仅是个开始。
因此,下一个要点:如果您更改数据源位置,我们的控件将不会更改它。货币管理器具有PositionChanged
事件。我们将使用它。
this.listManager.PositionChanged +=
new EventHandler(listManager_PositionChanged);
在起点,我们根据位置向它们添加了位置和节点的索引。这使我们可以通过其位置轻松找到节点。因此,这段简短的代码将使我们能够按其位置找到选定的节点。
DataTreeViewNode node =
this.items_Positions[this.listManager.Position] as DataTreeViewNode;
if (node != null)
{
this.SelectedNode = node;
}
当前上下文位置
现在您不能将此控件用作表的父项。基本上,我们需要做的是根据节点的选择来更改上下文的位置。这不是问题,因为我们将项目的位置存储在每个节点中。发出AfterSelect
事件
private void DataTreeView_AfterSelect(object sender,
System.Windows.Forms.TreeViewEventArgs e)
{
DataTreeViewNode node = e.Node as DataTreeViewNode;
if (node != null)
{
//Set position.
this.listManager.Position = node.Position;
}
}
标签编辑
没问题!只需使用AfterLabelEdit
。
private void DataTreeView_AfterLabelEdit(object sender,
System.Windows.Forms.NodeLabelEditEventArgs e)
{
DataTreeViewNode node = e.Node as DataTreeViewNode;
if (node != null)
{
//This will found appropriate converter for
//type and see if it can convert from string.
if (this.PrepareValueConvertor()
&& this.valueConverter.IsValid(e.Label)//Lets converter to check value.
)
{
//Set property.
this.nameProperty.SetValue(
this.listManager.List[node.Position],
this.valueConverter.ConvertFromString(e.Label)
);
this.listManager.EndCurrentEdit();
return;
}
}
//Node text are not editable.
e.CancelEdit = true;
}
Using the Code
作为DataSource
,您可以使用任何数据,例如DataGrid
可以使用的数据。您可以使用任何类型的列进行绑定。基本上,只有名称列仅限于可以从string
转换的类型。这仅在EditLabel
为true
时适用。
在大多数情况下,数据必须包含三列:标识符、名称和父行的标识符。如果您需要像'FirstName + " " + LastName'这样的Name字段 - 您可以在DataSet
中创建自动计算列。
我没有在绑定中包含图像索引,因为我不需要它。如果您需要此功能,请告诉我。我将更新这篇文章。
奖励
第一个好处是完整的设计时支持。与“Code Project”上的所有其他数据绑定树不同,此树视图具有所有标准设计器,例如DataGrid
具有的所有标准设计器(当然,有一些更改,请参阅Design
命名空间)。第二个是roundup框架错误,TreeView中的底部滚动条(滚动条始终可见,即使根本不需要它)。
就这样!尽情享受吧。访问我的博客以获取最新消息!