Заполняем TreeView в коде
Предоставим пользователю нашего приложения возможность, запустив проект, самому заполнять произвольное дерево, причем так, как это делается в Редакторе вершин дерева. Для этого достаточно запрограммировать работу 4 кнопок, которые вы видите на Рис. 20.16. Все кнопки действуют по отношению к любой выделенной вершине и смысл их ясен без объяснений. Текстовое поле служит для ввода названия вершины.
Но предварительно мы должны кое-что о дереве узнать. Сначала о терминологии.
Вершина – человек. От человека тянутся вниз и направо ветки к его сыновьям (так называемые дочерние вершины – Child). По отношению к сыновьям родитель является родительской вершиной (Parent). Адам – корень. Нажимая в режиме проектирования кнопку Add Root, мы можем добавить к дереву сколько угодно корней. Братья по отношению друг к другу называются Sibling.
Свойства и методы дерева и вершин. Каждая вершина является объектом класса TreeNode. Каждая вершина обладает свойством – коллекцией Nodes – это дочерние вершины (только дочерние, без внуков, правнуков и более отдаленных потомков). Сколько вершин на дереве – столько и коллекций. Но некоторые коллекции – пустые (у кого-то нет детей). Каждый элемент коллекции – это тоже объект класса TreeNode, то есть вершина. Вот такая, довольно сложная, но стройная картина получается.
У каждой вершины есть метод Remove – он уничтожает вершину со всеми ее потомками. Есть свойство Parent – это родительская вершина. Каждую вершину можно выделить своим цветом фона (свойство BackColor) и цветом шрифта (свойство ForeColor). Метод Expand осуществляет нажатие на плюсик у вершины, то есть показывает ее дочерние вершины. Метод ExpandAll показывает не только дочерние вершины, но всех потомков.
Также у вершины есть метод GetNodeCount, который сообщает нам количество вершин-потомков данной вершины. У метода есть булевский параметр: True указывает, что считать нужно всех потомков, False – только дочерние вершины.
Методы Expand и ExpandAll, а также GetNodeCount есть и у дерева TreeView. Здесь True указывает, что считать нужно все вершины дерева, False – только корни.
У дерева TreeView есть также свойство SelectedNode. Оно имеет своим значением выделенную вершину.
Простой вариант программы. Создайте проект с TreeView, 4 кнопками и текстовым полем. TreeView не заполняйте, он должен до запуска проекта быть пустым. Введите в окно кода следующие 5 процедур. Сразу же оговорюсь, что текст процедур данного варианта максимально упрощен. Я жертвовал целесообразностью и надежностью ради понятности:
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
TV.Nodes.Add("Адам")
End Sub
Private Sub Создай_сына_Click(ByVal sender As Object, ByVal e As EventArgs) Handles Создай_сына.Click
Dim Вершина As New TreeNode(TextBox1.Text)
TV.SelectedNode.Nodes.Add(Вершина)
End Sub
Private Sub Создай_брата_Click(ByVal sender As Object, ByVal e As EventArgs) Handles Создай_брата.Click
Dim Вершина As New TreeNode(TextBox1.Text)
TV.SelectedNode.Parent.Nodes.Add(Вершина)
End Sub
Private Sub Переименуй_Click(ByVal sender As Object, ByVal e As EventArgs) Handles Переименуй.Click
TV.SelectedNode.Text = TextBox1.Text
End Sub
Private Sub Удали_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Удали.Click
TV.SelectedNode.Remove()
End Sub
Пояснения: У коллекции Nodes, как и у всякой коллекции, есть метод Add, который добавляет в коллекцию элемент – объект класса TreeNode. В качестве параметра метода мы можем указать свойство Text
этого объекта (в процедуре Form1_Load мы указали "Адам") или заранее созданный объект класса TreeNode (так мы делаем в других процедурах проекта).
Первой выполняется процедура Form1_Load. В пустом дереве она создает вершину Адам. Смысла в этом нет, но мне так проще объяснять.
Не надо сразу же нажимать на кнопку Удали. Поскольку кроме Адама других вершин на дереве нет, именно она и будет выбранной (выделенной темным цветом), а значит будет удалена процедурой Удали_Click. Дерево опустеет и дальше нам не с чем будет работать.
Переименуйте эту вершину, набрав текст в текстовом поле и нажав кнопку Переименуй. Поскольку кроме Адама других вершин на дереве нет, именно она и будет выбранной, а значит будет переименована процедурой Переименуй_Click.
Щелкните по кнопке Создай сына. Первым выполнится оператор
Dim Вершина As New TreeNode(TextBox1.Text)
Он создает объект Вершина класса TreeNode. Свойство Text
этого объекта определяется значением параметра. У нас это текст в текстовом поле.
Вторым выполнится оператор
TV.SelectedNode.Nodes.Add(Вершина)
Поскольку выделен Адам, то к коллекции Nodes его сыновей (пустой пока), добавляется только что созданная вершина, что вы и увидите сразу же на экране, щелкнув по появившемуся плюсику. Щелкните по кнопке Создай сына несколько раз. На экране вы увидите несколько сыновей Адама.
Выделите одного из сыновей и щелкните по кнопке Создай сына. Теперь выделен сын и поэтому вершина будет добавлена к коллекции Nodes сына, а не Адама. На экране появится внук.
Выделяя того или иного из сыновей или внуков, щелкайте по кнопке Создай сына, предварительно вводя разный текст в текстовое поле. Вы увидите, как на дереве появляются внуки, правнуки и более отдаленные потомки.
Кнопка Создай брата вроде бы ни к чему, и без нее все работает. Но она мне пригодилась для объяснения свойства Parent. Выделите одну из вершин (но не корень) и нажмите кнопку. В операторе
TV.SelectedNode.Parent.Nodes.Add(Вершина)
мы видим вот что:
TV.SelectedNode |
Выбранная вершина |
TV.SelectedNode.Parent |
Родитель выбранной вершины |
TV.SelectedNode.Parent.Nodes |
Коллекция сыновей родителя выбранной вершины, то есть братья выбранной вершины |
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Dim Вершина As TreeNode
For Each Вершина In TV.Nodes
Вершина.Remove()
Next
End Sub
Private Sub Создай_сына_Click( ByVal sender As Object, ByVal e As EventArgs) Handles Создай_сына.Click
Dim Вершина As New TreeNode(TextBox1.Text)
If TV.GetNodeCount(True) = 0 Then
TV.Nodes.Add(Вершина)
TV.Focus()
ElseIf TV.SelectedNode Is Nothing Then
MsgBox("Выберите вершину")
Else
TV.SelectedNode.Nodes.Add(Вершина)
Вершина.Parent.Expand()
End If
End Sub
Private Sub Создай_брата_Click(ByVal sender As Object, ByVal e As EventArgs) Handles Создай_брата.Click
Dim Вершина As New TreeNode(TextBox1.Text)
If TV.GetNodeCount(True) = 0 Then
TV.Nodes.Add(Вершина)
TV.Focus()
ElseIf TV.SelectedNode Is Nothing Then
MsgBox("Выберите вершину")
ElseIf TV.SelectedNode.Parent Is Nothing Then
TV.Nodes.Add(Вершина)
Else
TV.SelectedNode.Parent.Nodes.Add(Вершина)
End If
End Sub
Private Sub Переименуй_Click(ByVal sender As Object, ByVal e As EventArgs) Handles Переименуй.Click
If TV.GetNodeCount(True) = 0 Then
MsgBox("Дерево пусто")
ElseIf TV.SelectedNode Is Nothing Then
MsgBox("Выберите вершину")
Else
TV.SelectedNode.Text = TextBox1.Text
End If
End Sub
Private Sub Удали_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Удали.Click
If TV.GetNodeCount(True) = 0 Or TV.SelectedNode Is Nothing Then Exit Sub
TV.SelectedNode.Remove()
End Sub
Пояснения: Процедуре Form1_Load я придумал другое дело – теперь она очищает дерево, если вы его нечаянно заполнили в режиме проектирования. Поскольку метод Remove уничтожает вершину со всеми ее потомками, достаточно уничтожить все корни дерева, то есть элементы коллекции TV.Nodes, что и делается.
Теперь разберем процедуру для кнопки Переименуй. Ее работа – ключ к пониманию работы других процедур. Переименовывать имеет смысл только тогда, когда дерево не пусто и когда на дереве имеется выделенная вершина. Оператор If в этой процедуре создан для обработки именно такой ситуации, и его можно перевести так:
Если общее количество вершин дерева = 0, то
Сообщай, что дерево пусто
Иначе если не существует выбранной вершины, то
Сообщай, что надо бы выбрать вершину
Иначе
Переименовывай вершину.
Если объект не существует, то в VB говорят, что он «есть ничто», по-английски –
Is Nothing
Поэтому выражение
TV.SelectedNode Is Nothing
можно перевести, как «выбранная вершина есть ничто» или «выбранная вершина не существует».
Логика остальных кнопок ясна из их кода. Кнопки Создай сына и Создай брата при пустом дереве добавляют корень, как я и хотел. Строка
TV.Focus()
возвращает фокус на дерево, что удобно, но не обязательно.
В процедуре для кнопки Создай брата строка
ElseIf TV.SelectedNode.Parent Is Nothing Then
обрабатывает следующую ситуацию: Если у выбранной вершины не существует родителя, значит это корень, значит брат ему – тоже корень, значит создавать этого брата придется строкой
TV.Nodes.Add(Вершина)
В процедуре для кнопки Создай сына строка
Вершина.Parent.Expand()
избавляет от необходимости жать плюсик на родителе, чтобы увидеть только что созданного сына.
Прочее. Теперь было бы неплохо, чтобы работа пользователя по созданию дерева не пропала даром, а для этого нужно дать ему возможность сохранить созданное дерево, а потом загрузить. Но это нетривиальная задача. Отложите ее на будущее. Я ее разбирать не буду, но идею ее решения вы можете почерпнуть в следующем подразделе.