科技行者

行者学院 转型私董会 科技行者专题报道 网红大战科技行者

知识库

知识库 安全导航

至顶网软件频道基础软件Windows窗体控件开发示例:扩展TreeView

Windows窗体控件开发示例:扩展TreeView

  • 扫一扫
    分享文章到微信

  • 扫一扫
    关注官方公众号
    至顶头条

讲述了如何向 TreeView 控件添加数据绑定功能,它是一系列 Microsoft Windows 控件开发示例之一......

作者:佚名 来源:Microsoft 2007年11月14日

关键字:

  • 评论
  • 分享微博
  • 分享邮件
添加显示和值成员属性

  拥有 DataSource 是实现复杂数据绑定的第一步,但该控件需要了解数据的哪些特定字段或属性将用作显示和值成员。Display 成员将用作树节点的标题,而 Value 成员可通过节点的 Value 属性进行访问。这些属性都是字符串,表示字段或属性名,可以方便地添加到控件中:

Private m_ValueMember As String
Private m_DisplayMember As String

_
Public Property ValueMember() As String
Get
Return m_ValueMember
End Get
Set(ByVal Value As String)
m_ValueMember = Value
End Set
End Property

_
Public Property DisplayMember() As String
Get
Return m_DisplayMember
End Get
Set(ByVal Value As String)
m_DisplayMember = Value
End Set
End Property

  在此 TreeView 中,这些属性将仅表示叶节点的 DisplayValue 成员,每个分组级别的相应信息将在 AddGroup 方法中指定。

  使用 CurrencyManager 对象

  在前面探讨的 DataSource 属性中,创建了一个 CurrencyManager 类的实例,并存储在类级别变量中。通过该对象访问的 CurrencyManager 类是实现数据绑定的关键部分,因为它具有的属性、方法和事件可实现以下功能:

  • 访问数据源的基础 IList 对象
  • 在数据源中检索和设置对象字段或属性,以及
  • 使您的控件与同一窗体中的其他数据绑定控件同步。
  检索属性/字段值

  CurrencyManager 对象允许您通过它的 GetItemProperties 方法从数据源的单个项中检索属性或字段值,如 DisplayMemberValueMember 字段的值。然后使用 PropertyDescriptor 对象获取特定列表项上的特定字段或属性的值。下面的代码片断显示了这些 PropertyDescriptor 对象的创建方法以及如何使用 GetValue 函数获取基础数据源中某一项的属性值。请注意 CurrencyManager 对象的 List 属性:通过它可以访问该控件绑定到的 IList 实例:

Dim myNewLeafNode As TreeLeafNode
Dim currObject As Object
currObject = 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 类(TreeLeafNodeTreeGroupNode),并将每个节点的列表索引存储到创建的 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 If
End Function

  设置 CurrencyManager 对象的 Position 属性可使其他控件与当前选定项同步,但是当其他控件的位置发生变化时,CurrencyManager 也产生事件,以便相应地更改选定项。要成为一个优秀的数据绑定组件,所选内容应随着数据源位置的更改而移动,修改某一项的数据时,显示应随之更新。CurrencyManager 引发的事件共有三个:CurrentChangedItemChangedPositionChanged。最后一个事件相当简单;CurrencyManager 的用途之一是为数据源维护当前位置指示器,以便多个绑定控件均可以显示同一记录或列表项,只要该位置更改,此事件便会引发。其他两个事件有时会相互重叠,因而区别不太明显。以下分别介绍如何在自定义控件中使用这些事件:PositionChanged 是一个比较简单的事件,此处不再赘述;当您要在复杂数据绑定控件(如 Tree)中调整当前选定项时,请使用该事件。只要修改数据源中的项,ItemChanged 事件就会引发,而 CurrentChanged 只有在当前项被修改时才引发。

  在我的 TreeView 中,我发现每当我选择一个新项时,所有三个事件均会引发,因此我决定通过更改当前选定项来处理 PositionChanged 事件,而对另外两项不进行任何处理。建议将数据源强制转换为 IBindingList(如果数据源支持 IBindingList 的话)并改用 ListChanged 事件,但我未实现此功能。

Private Sub cm_PositionChanged(ByVal sender As Object, _
ByVal e As System.EventArgs) Handles cm.PositionChanged
Dim tln As TreeLeafNode
If TypeOf Me.SelectedNode Is TreeLeafNode Then
tln = CType(Me.SelectedNode, TreeLeafNode)
Else
tln = FindFirstLeafNode(Me.SelectedNode)
End If

If tln.Position <> cm.Position Then
Me.SelectedNode = FindNodeByPosition(cm.Position)
End If
End Sub

Private Overloads Function FindNodeByPosition(ByVal index As Integer) _
As TreeNode
Return FindNodeByPosition(index, Me.Nodes)
End Function

Private Overloads Function FindNodeByPosition(ByVal index As Integer, _
ByVal NodesToSearch As TreeNodeCollection) As TreeNode
Dim i As Integer = 0
Dim currNode As TreeNode
Dim tln As TreeLeafNode

Do While i < NodesToSearch.Count
currNode = NodesToSearch(i)
i += 1
If TypeOf currNode Is TreeLeafNode Then
tln = CType(currNode, TreeLeafNode)
If tln.Position = index Then
Return currNode
End If
Else
currNode = FindNodeByPosition(index, currNode.Nodes)
If Not currNode Is Nothing Then
Return currNode
End If
End If
Loop
Return Nothing
End Function

    • 评论
    • 分享微博
    • 分享邮件
    邮件订阅

    如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。

    重磅专题
    往期文章
    最新文章