30 янв. 2015 г.

VB .Net. Выгрузка и загрузка элемента TreeView через xml

В статье можно найти как выгрузить и загрузить данные элемента TreeView. Как вручную добавлять, изменять, переименовывать и удалять узлы в TreeView. Примеры кода на Visual Basic.net.

Итак, у нас на форме есть элемент управление TreeView, у меня он для краткости называется "TV". Его можно заполнить просто в конструкторе, а можно и вручную (об этом ниже). Но при закрытии формы все изменения очистятся, хотелось бы сохранить их, а потом загрузить. Для этого сначала создадим временный TreeView, а потом с помощью SoapFormatter сериализуем наше дерево в файл xml (для выгрузки), и десереализуем (для загрузки). 
Сереализация - это что-то типа перевод данных в последовательности битов. А десериализация - обратный процесс, перевод битов в структуру данных.


Сохранение TreeView в xml


Dim tempNodes() = New TreeNode(TV.Nodes.Count - 1) {} 'создание временного TreeView

'перегружаем из нашего TreeView, что на форме во временный TreeView
Dim i As Integer
For i = 0 To TV.Nodes.Count - 1
tempNodes(i) = TV.Nodes(i)
Next i

'создаем новый объект класса FileStream
Dim fs As New FileStream("TreeSave.xml", FileMode.Create)
Dim sf As New SoapFormatter()
'с помощью SoapFormatter записываем наш временный TreeView в xml
sf.Serialize(fs, tempNodes)
fs.Close()


Загрузка TreeView с xml


Dim tempNodes As TreeNode() 'создание временного TreeView
'будем считывать наш файлик xml
'сейчас он сохраняется в папке с программой
'вы можете использовать OpenFileDialog и открывать файл, где бы он не был
Dim fs As New FileStream("TreeSave.xml", FileMode.Open)
'так же, как и в выгрузке (сохранении) будем использовать SoapFormatter
Dim sf As New SoapFormatter()
'только там сериализовали, а тут десереализуем файл xml
tempNodes = DirectCast(sf.Deserialize(fs), TreeNode())
'добавляем в элемент TreeView (что на форме) наш массив узлов дерева
TV.Nodes.AddRange(tempNodes)
fs.Close()

Следующие процедуры совершаются самыми простыми способами. То есть, для добавления пункта/подпункта, их переименования используется InputBox. При желании можно использовать отдельную, красивую и удобную формочку. Где будут TextBoxes, Buttons, Labels и т.д., в зависимости от ваших желаний и потребностей.

Добавление пункта в TreeView


'Если TreeView пустой - тогда добавляем пункт
If TV.GetNodeCount(True) = 0 Then
AddItem() 'эта процедура описана ниже!!!
ElseIf TV.SelectedNode Is Nothing Then 'если не выбран пункт - выход
Exit Sub
ElseIf TV.SelectedNode.Parent Is Nothing Then 'если у выделенного пункта нет родителя - добавляем пункт в конец узлов дерева
AddItem()
Else 'иначе - добавляем пункт (выбранному пункту - брата)
TV.SelectedNode.Parent.Nodes.Add(InputBox("Добавление пункта"))
TV.Focus()
End If


Добавление подпункта в TreeView


'Если TreeView пустой - тогда добавляем пункт в конец узлов
If TV.GetNodeCount(True) = 0 Then
AddItem()
ElseIf TV.SelectedNode Is Nothing Then 'если не выбран пункт - выход
Exit Sub
Else
'объявляем новый узел
Dim NewSubItem As New TreeNode(InputBox("Добавление ПОДпункта"))
'добавляем пункт (выбранному пункту - сына)
       TV.SelectedNode.Nodes.Add(NewSubItem)
       'разворачиваем родителя нашего подпункта
       NewSubItem.Parent.Expand()
       'выбираем наш новый подпункт
       TV.SelectedNode = NewSubItem
       TV.Focus()
End If

Процедура AddItem() (нужна для кода выше)


Sub AddItem() 'процедура добавления пункта в конец узлов дерева
       Dim NewItem As New TreeNode(InputBox("Добавление пункта"))
       TV.Nodes.Add(NewItem)
       TV.SelectedNode = NewItem
       TV.Focus()
End Sub


Переименование пункта (узла) в TreeView


'если узлов у дерева нет или не выбран пункт, тогда ничего не делаем
If TV.GetNodeCount(True) = 0 Or TV.SelectedNode Is Nothing Then Exit Sub
'вызываем InputBox c текстом, что был в пункте дерева
Dim NewName = InputBox("Переименование", , TV.SelectedNode.Text)
'проверка на пустой новый текст пункта
If NewName = "" Then Exit Sub
'если проверку прошли - присваиваем
TV.SelectedNode.Text = NewName
TV.Focus()


Удаление пункта (узла) с TreeView


Тут все просто. Если элементов нет или ничего не выделено, тогда выходим из процедуры. Иначе задаем уточняющий вопрос. Если ответ - "Да", тогда удаляем.

If TV.GetNodeCount(True) = 0 Or TV.SelectedNode Is Nothing Then Exit Sub
Dim Que As Byte
Que = MsgBox("Точно удалить?", vbYesNo)
'смотрим, какую кнопку нажал пользователь
If Que = vbYes Then TV.SelectedNode.Remove()

Кроме добавления, удаления, переименовывания, узлы TreeView также можно таскать мышей вверх-вниз. Оригинал кода был взят с MSDN и немного переработан под себя. Код "под себя" , с учетом выше наведенного кода и с комментариями на русском привожу ниже, может кому-то будет полезно.


Перемещение, копирование элементов


Если что, копирование осуществляется нажатием правой кн. мыши и перетаскиванием узла.
Для реализации перетаскивания, копирования узлов достаточно скопировать код, что ниже в модуль формы вашего приложения. На форме должен быть TreeView (TreeView.Name = "TV").

'ПЕРЕМЕЩЕНИЕ ЭЛЕМЕНТОВ
    Private Sub TV_ItemDrag(sender As Object, e As ItemDragEventArgs) Handles TV.ItemDrag
        'перемещаем перетаскиваемый узел, если нажата левая кн. мыши
        If e.Button = MouseButtons.Left Then
            DoDragDrop(e.Item, DragDropEffects.Move)

            'копируем перетаскиваемый узел, если нажата левая кн. мыши
        ElseIf e.Button = MouseButtons.Right Then
            DoDragDrop(e.Item, DragDropEffects.Copy)
        End If
    End Sub

    Private Sub TV_DragEnter(ByVal sender As Object, ByVal e As DragEventArgs) Handles TV.DragEnter
        e.Effect = e.AllowedEffect
    End Sub

    Private Sub TV_DragOver(ByVal sender As Object, ByVal e As DragEventArgs) Handles TV.DragOver
        'извлекаем координаты клиента с позиции мыши
        Dim targetPoint As Point = TV.PointToClient(New Point(e.X, e.Y))

        'выбираем узел исходя из позиции мыши
        TV.SelectedNode = TV.GetNodeAt(targetPoint)
    End Sub

    Private Sub TV_DragDrop(ByVal sender As Object, ByVal e As DragEventArgs) Handles TV.DragDrop

        'извлекаем координаты клиента с места остановки
        Dim targetPoint As Point = TV.PointToClient(New Point(e.X, e.Y))

        'извлекаем узел с места остановки
        Dim targetNode As TreeNode = TV.GetNodeAt(targetPoint)

        'извлекаем узел который был перемещен
        Dim draggedNode As TreeNode = CType(e.Data.GetData(GetType(TreeNode)), TreeNode)

        'убеждаемся, что узел с места остановки не
        'перемещаемый узел или потомок перемещаемого узла
        If Not draggedNode.Equals(targetNode) AndAlso Not ContainsNode(draggedNode, targetNode) Then

            'если это операция перемещения, удаляем узел из его текущей
     'позиции и добавляем его к узлу в конечной локации 
            If e.Effect = DragDropEffects.Move Then
                draggedNode.Remove()
                targetNode.Nodes.Add(draggedNode)

                'если это операция копирования – клонируем перетаскиваемый элемент 
                'и добавляем его в конечную позицию перетаскивания
            ElseIf e.Effect = DragDropEffects.Copy Then
                targetNode.Nodes.Add(CType(draggedNode.Clone(), TreeNode))
            End If

            'разворачиваем узел, чтобы показать его в новой позиции
            targetNode.Expand()
        End If
    End Sub

    Private Function ContainsNode(ByVal node1 As TreeNode, ByVal node2 As TreeNode) As Boolean
        'выбираем родительский узел со второго узла
        If node2.Parent Is Nothing Then
            Return False
        End If
        If node2.Parent.Equals(node1) Then
            Return True
        End If

        'если родительский узел не null или равен первому узлу 
        'рекурсивно вызываем функцию ContainsNode используя родителя второго узла
        Return ContainsNode(node1, node2.Parent)
    End Function

2 комментария:

  1. Не работает перемещение и копирование, всегда показывает крест

    ОтветитьУдалить

Спрашивайте, критикуйте, оставьте свое мнение