保持數據綁定控件同步
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 If
End Function
設置 CurrencyManager 對象的 Position 屬性可使其他控件與當前選定項同步,但是當其他控件的位置發生變化時,CurrencyManager 也產生事件,以便相應地更改選定項。要成為一個優秀的數據綁定組件,所選內容應隨著數據源位置的更改而移動,修改某一項的數據時,顯示應隨之更新。CurrencyManager 引發的事件共有三個:CurrentChanged、ItemChanged 和 PositionChanged。最後一個事件相當簡單;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