Tipp-Upload: VB.NET 0145: TreeviewEditor
von Spatzenkanonier
Über den Tipp
Dieser Tippvorschlag wird übernommen.
Der Vorschlag ist in den folgenden Kategorien zu finden:
- Steuerelemente
Dem Tippvorschlag wurden folgende Schlüsselwörter zugeordnet:
Xml,Drag,DragnDrop,Ini
Der Vorschlag wurde erstellt am: 12.11.2007 01:28.
Die letzte Aktualisierung erfolgte am 03.06.2009 10:51.
Beschreibung
Unterstützt DragnDrop mit Treenodes, und Abspeichern / Laden von Treenode-Eigenschaften mit Xml.
Die Möglichkeit, auch Baumstrukturen abzuspeichern ist ein wesentlicher Vorzug von Xml-Dateien gegenüber Ini-Dateien, und auch gegenüber den (ansonsten fabelhaft komfortablen) Konfigurationseinstellungen der Projekteigenschaften.
Ein DragDrop ohne Seiteneffekt im DragOver-Event wird in Tippvorschlag 372 gezeigt
Schwierigkeitsgrad |
Verwendete API-Aufrufe: |
Download: |
' Dieser Source stammt von http://www.activevb.de ' und kann frei verwendet werden. Für eventuelle Schäden ' wird nicht gehaftet. ' Um Fehler oder Fragen zu klären, nutzen Sie bitte unser Forum. ' Ansonsten viel Spaß und Erfolg mit diesem Source! ' ' Beachten Sie, das vom Designer generierter Code hier ausgeblendet wird. ' In den Zip-Dateien ist er jedoch zu finden. ' --------- Anfang Projektgruppe TreeviewEditor.sln --------- ' -------- Anfang Projektdatei TreeviewEditor.vbproj -------- ' ------------ Anfang Datei frmTreeviewEditor.vb ------------ ' IDE-Voreinstellungen: ' Option Strict On ' Option Explicit On ' Projekt-Voreinstellungen: ' Imports System.Windows.Forms ' Imports Microsoft.VisualBasic.ControlChars ' Steuerelemente - Einstellungen: ' Treeview: Treeview1.AllowDrop = True Imports System.Xml Imports System.io Imports TreeviewEditor.TextDts Public Class frmTreeviewEditor Private ReadOnly _XIniName As String Private _Src, _Target As TreeNode ' Draggen innerhalb einer Anwendung ist ohne DataObject einfacher und sicherer ' Aber Control.DoDragDrop() verlangt eines Private Shared _dumData As New DataObject ' im Grunde kann bei .DoDragDrop() nur AllEffects angegeben werden, denn beim Drag-Start kann ' der AllowedEffekt nicht eingeschränkt werden: Derselbe Ziehvorgang kann auf dem einen ' ZielControl ausschließlich .Copy erfordern, und auf dem anderen ausschließlich .Move. ' Es gibt zwar einen DragDropEffects.All, aber wenn man den verwendet, kann ' DragDropEffects.Link nicht eingestellt werden. Private Shared _allEffects As DragDropEffects = DragDropEffects.Move Or _ DragDropEffects.Copy Or DragDropEffects.Link Or DragDropEffects.Scroll Public Sub New() InitializeComponent() Me.TreeView1.TreeViewNodeSorter = New ComparisonComparer(Of TreeNode)(AddressOf Compare) ' ("My.Settings.XIniName" wurde in den Projekteigenschaften gesetzt.) _XIniName = Path.GetFullPath(My.Settings.XIniName) TreeView1.ExpandAll() End Sub Private Function Compare(ByVal x As TreeNode, ByVal y As TreeNode) As Integer Return String.Compare(x.Text, y.Text) End Function #Region "LoadSaveTreeview" Private Sub MenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _ Handles SaveToolStripMenuItem.Click, _ ReloadToolStripMenuItem.Click, _ DeleteToolStripMenuItem.Click Select Case True Case sender Is ReloadToolStripMenuItem ReloadIni() Case sender Is SaveToolStripMenuItem SaveIni() Case sender Is DeleteToolStripMenuItem If File.Exists(_XIniName) Then File.Delete(_XIniName) End Select End Sub Private Sub ReloadIni() If Not File.Exists(_XIniName) Then MsgBox(String.Concat("Ini-File", Lf, _XIniName, Lf, "konnte nicht gefunden werden")) Return End If TreeView1.Nodes.Clear() Dim XDoc As New XmlDocument XDoc.Load(_XIniName) BuildNodes(XDoc.DocumentElement, Me.TreeView1.Nodes) End Sub Private Sub BuildNodes(ByVal XParent As XmlElement, ByVal Nodes As TreeNodeCollection) For Each XChild As XmlElement In XParent.ChildNodes Dim ndNew As New TreeNode(XChild.GetAttribute("NodeText")) Nodes.Add(ndNew) BuildNodes(XChild, ndNew.Nodes) If XChild.HasAttribute("IsExpanded") Then If Boolean.Parse(XChild.GetAttribute("IsExpanded")) Then ndNew.Expand() End If Next End Sub Private Sub SaveIni() Dim XDoc As New XmlDocument BuildXElements(AppendNode(XDoc, XDoc.CreateElement("Root")), TreeView1.Nodes) XDoc.Save(_XIniName) End Sub Private Sub BuildXElements(ByVal XParent As XmlElement, ByVal Nodes As TreeNodeCollection) For Each ndChild As TreeNode In Nodes Dim XChild As XmlElement = XParent.OwnerDocument.CreateElement("Treenode") With AppendNode(XParent, XChild) .SetAttribute("NodeText", ndChild.Text) .SetAttribute("IsExpanded", ndChild.IsExpanded.ToString) BuildXElements(XChild, ndChild.Nodes) End With Next End Sub #End Region ' LoadSaveTreeview #Region "DragnDrop" ''' <summary>Dragging starten **und** Ergebnis auswerten</summary> Private Sub TreeView1_ItemDrag(ByVal sender As Object, ByVal e As ItemDragEventArgs) _ Handles TreeView1.ItemDrag _Src = DirectCast(e.Item, TreeNode) TreeView1.AllowDrop = True Dim ndNew As TreeNode = Nothing Try Select Case TreeView1.DoDragDrop(_dumData, _allEffects) Case DragDropEffects.Copy ndNew = New TreeNode(_Src.Text) Case DragDropEffects.Move ndNew = _Src _Src.Remove() Case Else Return End Select GetNodes(_Target).Add(ndNew) ndNew.EnsureVisible() Finally ' Drag-Vorgänge, die von woanders ausgehen, für diesen Treeview unterbinden TreeView1.AllowDrop = False End Try End Sub Private Sub TreeView1_DragOver(ByVal sender As Object, ByVal e As DragEventArgs) _ Handles TreeView1.DragOver Try With TreeView1 _Target = .GetNodeAt(.PointToClient(New Point(e.X, e.Y))) End With ' Drop-Effekt aufgrund Control-Tasten-Status festlegen Select Case Control.ModifierKeys Case Keys.None e.Effect = DragDropEffects.Move ' Drop-Effekt aufgrund des DropTargets nochmal validieren ' Src nicht in eigenen Parent ablegen If _Src.Parent Is _Target Then e.Effect = DragDropEffects.None Return End If ' Src nicht in sich selbst oder eigene Children ablegen Dim ndTemp As TreeNode = _Target Do Until ndTemp Is Nothing If ndTemp Is _Src Then e.Effect = DragDropEffects.None Return End If ndTemp = ndTemp.Parent Loop Case Keys.Shift e.Effect = DragDropEffects.Copy Case Else e.Effect = DragDropEffects.None Return End Select Finally ' Finally ohne -Catch garantiert, daß dieser Block durchlaufen wird, ' auch wenn im Try-Block Return aufgerufen wurde ' Hier wird als Markierung des DropTargets TreeView1.SelectedNode gesetzt. ' Das ist unsauber und riskant, weil dadurch das AfterSelect-Event ausgelöst wird, ' also an dieser Stelle nicht abschätzbare Seiteneffekte auftreten If e.Effect = DragDropEffects.None Then TreeView1.SelectedNode = Nothing Else TreeView1.SelectedNode = _Target End If End Try End Sub #End Region ' DragnDrop #Region "EditTreenodes" Private Sub ContextMenuStrip1_Opened(ByVal sender As Object, ByVal e As EventArgs) _ Handles ContextMenuStrip1.Opened ' etwas unsauber: _Target ist dasselbe, welches auch fürs Draggen genutzt wird ' Aber man kann ja nicht draggen und Kontextmenü öffnen gleichzeitig _Target = TreeView1.GetNodeAt(TreeView1.PointToClient(Control.MousePosition)) End Sub Private Sub ContextMenuItem_Click(ByVal sender As Object, ByVal e As EventArgs) _ Handles DeleteBranchToolStripMenuItem.Click, _ DeleteThisOnlyToolStripMenuItem.Click, _ InsertNodeToolStripMenuItem.Click Select Case True Case sender Is DeleteBranchToolStripMenuItem ' _Target mit Children removen If _Target Is Nothing Then Return _Target.Remove() Case sender Is DeleteThisOnlyToolStripMenuItem If _Target Is Nothing Then Return ' _Targets Children gehen an _Target.Parent Dim Nodes As TreeNodeCollection = GetNodes(_Target.Parent) For I As Integer = _Target.Nodes.Count - 1 To 0 Step -1 Dim Nd As TreeNode = _Target.Nodes(I) Nd.Remove() Nodes.Add(Nd) Next _Target.Remove() Case sender Is InsertNodeToolStripMenuItem ' Node creiern und zu editieren anfangen With GetNodes(_Target).Add(String.Concat("Knoten", TreeView1.GetNodeCount(True))) .EnsureVisible() .BeginEdit() End With End Select End Sub #End Region ' EditTreenodes ''' <summary>falls Nd is Nothing Treeview.Nodes returnen</summary> Private Function GetNodes(ByVal Nd As TreeNode) As TreeNodeCollection If Nd Is Nothing Then Return TreeView1.Nodes Return Nd.Nodes End Function Private Sub ReloadDatasetToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e _ As System.EventArgs) Handles ReloadDatasetToolStripMenuItem.Click, _ SaveDatasetToolStripMenuItem.Click Select Case True Case sender Is ReloadDatasetToolStripMenuItem ReloadDataset() Case sender Is SaveDatasetToolStripMenuItem SaveDataset() End Select End Sub Private Sub SaveDataset() Dim dts As New TextDts For Each nd As TreeNode In TreeView1.Nodes ' Dim rw As ChapterRow = dts.Chapter.NewChapterRow ' rw.Titel = nd.Text Dim rw As ChapterRow = dts.Chapter.AddChapterRow(nd.Text, Nothing) BuildRows(rw, nd.Nodes) Next dts.WriteXml("Dataset.xml") End Sub Private Sub BuildRows(ByVal rw As ChapterRow, ByVal Nodes As TreeNodeCollection) Dim tb As ChapterDataTable = DirectCast(rw.Table, ChapterDataTable) For Each ndChild As TreeNode In Nodes Dim rwChild As ChapterRow = tb.AddChapterRow(ndChild.Text, rw) BuildRows(rwChild, ndChild.Nodes) Next End Sub Private Sub ReloadDataset() TreeView1.Nodes.Clear() Dim dts As New TextDts dts.ReadXml("Dataset.xml") For Each rw As ChapterRow In dts.Chapter If rw.IsParentChapterIDNull Then Dim ndNew As TreeNode = TreeView1.Nodes.Add(rw.Titel) BuildNodesFromRows(rw, ndNew.Nodes) End If Next TreeView1.ExpandAll() End Sub Private Sub BuildNodesFromRows(ByVal rw As ChapterRow, ByVal Nodes As TreeNodeCollection) For Each rwChild As ChapterRow In rw.GetChapterRows Dim ndNew As TreeNode = Nodes.Add(rwChild.Titel) BuildNodesFromRows(rwChild, ndNew.Nodes) Next End Sub End Class ' ------------- Ende Datei frmTreeviewEditor.vb ------------- ' ---------------- Anfang Datei modHelpers.vb ---------------- Imports System.Xml Imports System.Collections ''' <summary> ''' IComparer-implementierender Wrapper um eine Comparison(Of T) ''' </summary> Public Class ComparisonComparer(Of T) Inherits Generic.Comparer(Of T) Private _Comparison As Comparison(Of T) Public Sub New(ByVal Comparison As Comparison(Of T)) _Comparison = Comparison End Sub Public Overrides Function Compare(ByVal x As T, ByVal y As T) As Integer Return _Comparison(x, y) End Function End Class Public Module modHelpers Public Function AppendNode(Of T As XmlNode)(ByVal XParent As XmlNode, ByVal XNew As T) As T XParent.AppendChild(XNew) Return XNew End Function End Module ' ----------------- Ende Datei modHelpers.vb ----------------- ' --------- Ende Projektdatei TreeviewEditor.vbproj --------- ' ---------- Ende Projektgruppe TreeviewEditor.sln ----------
Diskussion
Diese Funktion ermöglicht es, Fragen, die die Veröffentlichung des Tipps betreffen, zu klären, oder Anregungen und Verbesserungsvorschläge einzubringen. Nach der Veröffentlichung des Tipps werden diese Beiträge nicht weiter verlinkt. Allgemeine Fragen zum Inhalt sollten daher hier nicht geklärt werden.
Um eine Diskussion eröffnen zu können, müssen sie angemeldet sein.