答案:添加显示和值成员属性
拥有 DataSource 是实现复杂数据绑定的第一步,但该控件需要了解数据的哪些特定字段或属性将用作显示和值成员。Display 成员将用作树节点的标题,而 Value 成员可通过节点的 Value 属性进行访问。这些属性都是字符串,表示字段或属性名,可以方便地添加到控件中: Private m_ValueMember As String Private m_DisplayMember As String <Category("Data")> _ Public Property ValueMember() As String Get Return m_ValueMember End Get Set(ByVal Value As String) m_ValueMember = Value End Set End Property <Category("Data")> _ Public Property DisplayMember() As String Get Return m_DisplayMember End Get Set(ByVal Value As String) m_DisplayMember = Value End Set End Property
在此 TreeView 中,这些属性将仅表示叶节点的 Display 和 Value 成员,每个分组级别的相应信息将在 AddGroup 方法中指定。使用 CurrencyManager 对象
在前面探讨的 DataSource 属性中,创建了一个 CurrencyManager 类的实例,并存储在类级别变量中。通过该对象访问的 CurrencyManager 类是实现数据绑定的关键部分,因为它具有的属性、方法和事件可实现以下功能:
- 访问数据源的基础 IList 对象
- 在数据源中检索和设置对象字段或属性,以及
- 使您的控件与同一窗体中的其他数据绑定控件同步。
检索属性/字段值
CurrencyManager 对象允许您通过它的
GetItemProperties 方法从数据源的单个项中检索属性或字段值,如
DisplayMember 或
ValueMember 字段的值。然后使用
PropertyDescriptor 对象获取特定列表项上的特定字段或属性的值。下面的代码片断显示了这些
PropertyDescriptor 对象的创建方法以及如何使用
GetValue 函数获取基础数据源中某一项的属性值。请注意
CurrencyManager 对象的
List 属性:通过它可以访问该控件绑定到的
IList 实例:
Dim myNewLeafNode As TreeLeafNodeDim currObject As ObjectcurrObject = cm.List(currentListIndex)If Me.DisplayMember <> "" AndAlso Me.ValueMember <> "" Then ' 添加叶节点? Dim pdValue As System.ComponentModel.PropertyDescriptor Dim pdDisplay As System.ComponentModel.PropertyDescriptor pdValue = cm.GetItemProperties()(Me.ValueMember) pdDisplay = cm.GetItemProperties()(Me.DisplayMember) myNewLeafNode = _ New TreeLeafNode(CStr(pdDisplay.GetValue(currObject)), _ currObject, _ pdValue.GetValue(currObject), _ currentListIndex)
GetValue 在返回对象时忽略属性的基本数据类型,因此在使用返回值前需要对其进行转换。
保持数据绑定控件同步
CurrencyManager 还有一个主要功能:除了可以访问绑定数据源和项属性外,它还允许使用相同的
DataSource 来协调该控件和任何其他控件之间的数据绑定。该支持可用于确保多个同时绑定到同一数据源的控件停留在数据源的同一项。对于我的控件而言,我想确保在树中选择项时,其他所有绑定到同一数据源的控件均指向同一项(同一记录、行、甚至数组,如果您愿意从数据库的角度进行思考)。为此,我覆盖了基本
TreeView 中的
OnAfterSelect 方法。在该方法(在选择树节点后被调用)中,我将
CurrencyManager 对象的
Position 属性设置为当前选定项的索引。与该
TreeView 控件一起提供的示例应用程序阐释了同步控件如何使生成数据绑定用户界面变得更为容易。为了使确定当前选定项的列表位置更为容易,我使用了自定义
TreeNode 类(
TreeLeafNode 或
TreeGroupNode),并将每个节点的列表索引存储到创建的
Position 属性中:
Protected Overrides Sub OnAfterSelect _ (ByVal e As System.Windows.Forms.TreeViewEventArgs) Dim tln As TreeLeafNode If TypeOf e.Node Is TreeGroupNode Then tln = FindFirstLeafNode(e.Node) Dim groupArgs As New groupTreeViewEventArgs(e) RaiseEvent AfterGroupSelect(groupArgs) ElseIf TypeOf e.Node Is TreeLeafNode Then Dim leafArgs As New leafTreeViewEventArgs(e) RaiseEvent AfterLeafSelect(leafArgs) tln = CType(e.Node, TreeLeafNode) End If If Not tln Is Nothing Then If cm.Position <> tln.Position Then cm.Position = tln.Position End If End If MyBase.OnAfterSelect(e)End Sub
在前面的代码片段中,您可能注意到了一个称为
FindFirstLeafNode 的函数,在此我想对其加以简要介绍。在我的
TreeView 中,只有叶节点(分层结构中的最终节点)才与
DataSource 中的项相对应,其他所有节点只用于创建分组结构。如果我要创建一个性能优良的数据绑定控件,便始终需要选择一个与
DataSource 相对应的项,因此每当选择组节点时,我就会找到该组下的第一个叶节点,就好象该节点是当前的选定内容。您可以检查该示例的运行情况,但现在您大可放心地使用它。
Private Function FindFirstLeafNode(ByVal currNode As TreeNode) _ As TreeLeafNode If TypeOf currNode Is TreeLeafNode Then Return CType(currNode, TreeLeafNode) Else If currNode.Nodes.Count > 0 Then Return FindFirstLeafNode(currNode.Nodes(0)) Else Return Nothing End If End IfEnd Function
设置
CurrencyManager 对象的
Position 属性可使其他控件与当前选定项同步,但是当其他控件的位置发生变化时,
CurrencyManager 也产生事件,以便相应地更改选定项。要成为一个优秀的数据绑定组件,所选内容应随着数据源位置的更改而移动,修改某一项的数据时,显示应随之更新。
CurrencyManager 引发的事件共有三个:
CurrentChanged、
ItemChanged 和
PositionChanged。最后一个事件相当简单;
CurrencyManager 的用途之一是为数据源维护当前位置指示器,以便多个绑定控件均可以显示同一记录或列表项,只要该位置更改,此事件便会引发。其他两个事件有时会相互重叠,因而区别不太明显。以下分别介绍如何在自定义控件中使用这些事件:
PositionChanged 是一个比较简单的事件,此处不再赘述;当您要在复杂数据绑定控件(如 Tree)中调整当前选定项时,请使用该事件。只要修改数据源中的项,
ItemChanged 事件就会引发,而
CurrentChanged 只有在当前项被修改时才引发。
在我的
TreeView 中,我发现每当我选择一个新项时,所有三个事件均会引发,因此我决定通过更改当前选定项来处理
PositionChanged 事件,而对另外两项不进行任何处理。
转贴自MS:扩展 TreeView 控件 (1)下一个:
在VB.NET中定制一个3D的Label的源码