a亚洲精品_精品国产91乱码一区二区三区_亚洲精品在线免费观看视频_欧美日韩亚洲国产综合_久久久久久久久久久成人_在线区

首頁 > 編程 > Visual Basic > 正文

TreeView控件的設計實例

2023-06-12 12:11:28
字體:
來源:轉載
供稿:網友

本文重點講解了TreeView控件的使用方法。TreeView控件具有豐富的功能,可以運用到很多場合。摘要:講述了如何向 TreeView 控件添加數據綁定功能,它是一系列 Microsoft Windows 控件開發示例之一。您可以將本文與相關的概述文章結合起來閱讀。

簡介

在可能的情況下,您應該先使用些現成的控件;因為提供的 Microsoft® Windows® 窗體控件中包含大量編碼和測試成果,如果您要放棄它們從頭開始,無疑是一種巨大的浪費。基于此,在本例中,我將繼承一個現有 Windows 窗體控件 TreeView ,然后對其進行自定義。在下載該 TreeView 控件的代碼時,您還會得到附加的控件開發示例,以及一個演示如何與其他數據綁定控件一起使用該增強 TreeView 的示例應用程序。

設計數據綁定樹視圖

對于 Windows 開發人員來說,向 TreeView 控件添加數據綁定是經常會遇到的問題,但由于 TreeView 和其他控件(如 ListBoxDataGrid)存在一個主要差別(即 TreeView 顯示分層數據),因而基本控件目前還不支持此功能(也就是說,我們還必須使用它)。給定一個數據表,您就會很清楚如何在 ListBoxDataGrid 中顯示該信息,但利用 TreeView 的分層特點來顯示同樣的數據就不那么簡單明了。就個人而言,我在使用 TreeView 顯示數據時曾應用過許多不同的方法,但有一種方法最常用:按某些字段將表中的數據分組,如圖 1 所示。

圖 1:在 TreeView 中顯示數據

  在本例中,我將創建一個 TreeView 控件,在該控件中可傳遞一個平面數據集(如圖 2 所示),并可輕松地生成圖 1 所示的結果。

圖 2:平面結果集,包含創建圖 1 所示的樹所需的所有信息

  在開始編碼之前,我為新控件想出了一個可以處理該特定數據集的設計,并希望它能夠適用于許多其他類似的情形。添加一個足可以使用大多數平面數據創建分層結構的組集合,在該集合中為每一級分層均指定一個分組字段、顯示字段和值字段(任一或所有字段均應相同)。為了將圖 2 所示的數據轉變成圖 1 所示的 TreeView,我的新控件要求您定義兩個分組級別 Publisher 和 Title,并將 pub_id 定義為 Publisher 組的分組字段,將 title_id 定義為 Title 組的分組字段。除分組字段以外,還需要為每個組指定顯示和值字段,以確定在相應組節點上顯示的文本以及用來唯一標識特定組的值。當遇到此類數據時,請使用 pub_name/pub_idtitle/title_id 作為這兩個組的顯示/值字段。作者信息將變成樹的葉節點(分組分層結構末端的節點),您還需要為這些節點指定 ID (au_id) 和顯示 (au_lname) 字段。

  構建自定義控件時,在開始編碼之前確定程序員對該控件的使用方法將有助于提高控件的使用效率。這種情況下,我希望程序員(在給定了前面所示的數據和所需結果的情況下)能夠使用如下幾行代碼完成分組:

 

With DbTreeControl
.ValueMember = "au_id"
.DisplayMember = "au_lname"

.DataSource = myDataTable.DefaultView
.AddGroup("Publisher", "pub_id", "pub_name", "pub_id")
.AddGroup("Title", "title_id", "title", "title_id")
End With


  注意:這并不是我最終編寫的代碼行,但兩者相差不多。在開發控件的過程中,我意識到需要將與 TreeView 關聯的 ImageList 中的圖像索引與每個分組級別相關聯,因此必須向 AddGroup 方法中額外添加一個參數。

  為了真正構建該樹,我將瀏覽數據并查找字段(指定為每個分組的分組值)的更改,同時在必要時創建新分組節點,并針對每個數據項創建一個葉節點。由于存在分組節點,因此總節點數將大于數據源中的項目數,但基礎數據中的每個項有且僅有一個葉節點。

圖 3:分組節點與葉節點

  葉節點和分組節點之間的區別(如圖 3 所示)對本文的余下部分具有重要意義。我決定將這兩類節點區別對待,為每一類節點分別創建自定義節點,并根據所選的節點類型引發不同的事件。

  實現數據綁定

  為該控件編寫代碼的第一步是創建項目和相應的起始類。在本例中,我首先創建一個新 Windows 控件庫,然后刪除默認的 UserControl 類,并用一個從 TreeView 控件繼承的新類來代替它:
 

  Public Class dbTreeControl
   Inherits System.Windows.Forms.TreeView
 

  從這時起,我將設計一個可以放入到窗體中的控件,并使其具有常規的 TreeView 的外觀和功能。下一步是開始添加旨在處理在 TreeView 中加入的新功能所需的代碼,即數據綁定和分組數據。

  添加 DataSource 屬性

  我的新控件的所有功能都很重要,但構建復雜數據綁定控件的兩個關鍵問題是處理 DataSource 屬性和從數據源的每個對象中檢索單個項目。

  創建屬性例程

  首先,任何用于實現復雜數據綁定的控件都需要實現一個 DataSource 屬性例程,并保持適當的成員變量:

 

Private m_DataSource As Object

_
Public Property DataSource() As Object
Get
Return m_DataSource
End Get
Set(ByVal Value As Object)
If Value Is Nothing Then
cm = Nothing
GroupingChanged()
Else
If Not (TypeOf Value Is IList Or _
TypeOf Value Is IListSource) Then
' 不是針對該用途的有效數據源
Throw New System.Exception("無效 DataSource")
Else
If TypeOf Value Is IListSource Then
Dim myListSource As IListSource
myListSource = CType(Value, IListSource)
If myListSource.ContainsListCollection = True Then
Throw New System.Exception("無效 DataSource")
Else
' 對,對。它是有效的數據源
m_DataSource = Value
cm = CType(Me.BindingContext(Value), _
CurrencyManager)
GroupingChanged()
End If
Else
m_DataSource = Value
cm = CType(Me.BindingContext(Value), _
CurrencyManager)
GroupingChanged()
End If
End If
End If
End Set
End Property
 

  IList 接口

  可用作復雜數據綁定數據源的對象通常都支持,該接口將數據公開為對象集合,并提供若干有用屬性,如 Count。我的新 TreeView 控件要求在其綁定中使用一個支持 IList 的對象,但使用另一個接口也可以,因為它提供了一個獲取 IList 對象的簡便方法 (GetList)。當設置 DataSource 屬性后,我首先確定是否提供了有效的對象,即一個支持 IListIListSource 的對象。我真正想要的是 IList,因此如果對象僅支持 IListSource(例如 DataTable),那么我將使用該接口的 GetList() 方法獲得正確的對象。

  某些實現 IListSource 的對象(如 DataSet)實際上包含多個由 ContainsListCollection 屬性表示的列表。如果該屬性為 True,則 GetList 將返回一個表示列表(包含多個列表)的 IList 對象。在我的示例中,我決定支持直接連接到 IList 對象或僅包含一個 IList 對象的 IListSource 對象,并忽略需要附加工作來指定數據源的對象,如 DataSet

注意:如果要支持此類對象(DataSet 或與之類似的對象),您可以再添加一個屬性(如 DataMember)來指定用于綁定的特定子列表。

  如果提供的數據源有效,則最終結果是創建的實例 (cm = Me.BindingContext(Value))。由于該實例將用于訪問基礎數據源、對象屬性和位置信息,因此被存儲在局部變量中。

  添加顯示和值成員屬性

  擁有 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

   將 DataSource 轉變為樹

  編寫完數據綁定代碼后,我可以繼續添加管理分組級別的代碼,相應地生成樹,然后添加一些自定義事件、方法和屬性。

  管理組

  程序員要配置組集合,就必須創建 AddGroupRemoveGroupClearGroups 函數。每當修改組集合時,都必須重新繪制樹(以反映新配置),因此我創建了一個通用過程 GroupingChanged,當情況發生變化,需要強制重建樹時,它可以由控件中的各種代碼調用:

 

Private treeGroups As New ArrayList()

Public Sub RemoveGroup(ByVal group As Group)
If Not treeGroups.Contains(group) Then
treeGroups.Remove(group)
GroupingChanged()
End If
End Sub

Public Overloads Sub AddGroup(ByVal group As Group)
Try
treeGroups.Add(group)
GroupingChanged()
Catch
End Try
End Sub

Public Overloads Sub AddGroup(ByVal name As String, _
ByVal groupBy As String, _
ByVal displayMember As String, _
ByVal valueMember As String, _
ByVal imageIndex As Integer, _
ByVal selectedImageIndex As Integer)
Dim myNewGroup As New Group(name, groupBy, _
displayMember, valueMember, _
imageIndex, selectedImageIndex)
Me.AddGroup(myNewGroup)
End Sub


Public Function GetGroups() As Group()
Return CType(treeGroups.ToArray(GetType(Group)), Group())
End Function


  生成樹

  樹的實際重建由一對過程來完成:BuildTreeAddNodes。由于這兩個過程的代碼太長,本文并未全部列出,而是盡量概括它們的行為(當然,如果愿意您可以下載完整的代碼)。如前所述,程序員可以通過設置一系列組與該控件進行交互,然后在 BuildTree 中使用這些組來確定如何設置樹節點。BuildTree 清除當前節點集合,然后遍歷整個數據源來處理第一級分組(本文前面的示例和圖解中提到的 Publisher),為每個不同的分組值添加一個節點(使用示例中的數據,為每個 pub_id 值添加一個節點),然后調用 AddNodes 來填充第一級分組下的所有節點。AddNodes 遞歸調用自身以處理任意多的級數,必要時可添加組節點和葉節點。使用兩個基于 TreeNode 的自定義類以區別組節點和葉節點,并為兩類節點提供各自相應的屬性。

  自定義 TreeView 事件

  每當選擇一個節點時,TreeView 都會引發兩個事件:BeforeSelectAfterSelect。但在我的控件中,我想使組節點和葉節點的事件不同,于是便添加了自己的事件 BeforeGroupSelect/AfterGroupSelectBeforeLeafSelect/AfterLeafSelect,除基本事件外,還引發了自定義事件參數類:

 

Public Event BeforeGroupSelect _
(ByVal sender As Object, ByVal e As groupTreeViewCancelEventArgs)
Public Event AfterGroupSelect _
(ByVal sender As Object, ByVal e As groupTreeViewEventArgs)
Public Event BeforeLeafSelect _
(ByVal sender As Object, ByVal e As leafTreeViewCancelEventArgs)
Public Event AfterLeafSelect _
(ByVal sender As Object, ByVal e As leafTreeViewEventArgs)

Protected Overrides Sub OnBeforeSelect _
(ByVal e As System.Windows.Forms.TreeViewCancelEventArgs)
If TypeOf e.Node Is TreeGroupNode Then
Dim groupArgs As New groupTreeViewCancelEventArgs(e)
RaiseEvent BeforeGroupSelect(CObj(Me), groupArgs)
ElseIf TypeOf e.Node Is TreeLeafNode Then
Dim leafArgs As New leafTreeViewCancelEventArgs(e)
RaiseEvent BeforeLeafSelect(CObj(Me), leafArgs)
End If
MyBase.OnBeforeSelect(e)
End Sub

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(CObj(Me), groupArgs)
ElseIf TypeOf e.Node Is TreeLeafNode Then
Dim leafArgs As New leafTreeViewEventArgs(e)
RaiseEvent AfterLeafSelect(CObj(Me), 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

  自定義節點類(TreeLeafNodeTreeGroupNode)和自定義事件參數類均包括在可下載代碼中。

  示例應用程序

  要全面理解本示例控件中的所有代碼,您應該了解它在應用程序中的運行情況。包含的示例應用程序使用 pubs.mdb Access 數據庫,并說明 Tree 控件如何與其他數據綁定控件一起創建 Windows 應用程序。本例中,尤其值得注意的主要功能包括樹與其他綁定控件的同步以及對數據源執行搜索時樹節點的自動選擇。

注意:本示例應用程序(名為“TheSample”)包含在本文的下載中。

圖 4:數據綁定 TreeView 的演示應用程序

  小結

  本文介紹的數據綁定 Tree 控件并非適用于所有需要 Tree 控件來顯示數據庫信息的項目,但它確實介紹了一種可針對個人目的自定義該控件的方法。請記住,您要生成的任何復雜數據綁定控件與 Tree 控件的大部分代碼基本相同,您可以通過修改現有代碼來簡化以后的控件開發過程。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 午夜视| 在线播放国产一区二区三区 | 久久91久久久久麻豆精品 | 欧美一区二区三区在线观看 | 国产91一区 | 男人的天堂视频网站 | 国产精品福利在线观看 | 国产一区二区三区色淫影院 | 暖暖视频日韩欧美在线观看 | 成人a在线| 久热最新| 日韩美一级 | 国产精品一区在线观看 | 国产一区二区在线不卡 | 日韩在线视频免费观看 | 日本h视频在线观看 | 国产精品久久久久久久久久东京 | 久久久久国产精品 | 日韩成人影院在线观看 | 久久99久久98精品免观看软件 | 青青草视频免费观看 | 日本一区二区三区中文字幕 | 羞羞视频免费在线观看 | 免费成人在线观看视频 | 亚洲一区二区三区精品视频 | 国产精品一区二区在线播放 | 色呦呦一区| 亚洲污视频 | 中文字幕日韩欧美 | 成人黄色免费视频网站 | 99精品国产热久久91蜜凸 | 日韩欧美在线观看视频 | 国产精品久久一区 | 亚洲最黄视频 | 亚洲精品免费观看 | 免费久久精品 | 国产精品一区二 | av免费观看网站 | 在线欧美视频 | 男人天堂a| 精品久久久久一区二区国产 |