Die Community zu .NET und Classic VB.
Menü

Tipp-Upload: VB.NET 0145: TreeviewEditor

 von 

Ü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.

Zurück zur Übersicht

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

Schwierigkeitsgrad 2

Verwendete API-Aufrufe:

Download:

Download des Beispielprojektes [25,17 KB]

' 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.