Редактируемое дерево с меняющимся контекстным меню
На этой задаче я в очередной раз убедился, что далеко не все задачи в принципе реализуемы в Web. Задача управления классификаторами получается, увы, слишком тыжеловесна для Web. Поэтому значительную часть функций возможно выполнить ТОЛЬКО в Windows. Это происходит по двум причинам:
- Слишком много данных для передачи через Web. Скажем, типичное количество классифкаторов для среднего прайса - сто тысяч позиций. Для большого - раза в три больше. Даже для предварительной отладочной базы классифкаторов, над которой еще не работали менеджеры - получилось 60 тысяч записей для значений классифкаторов (по 850 классификаторам). Конечно, гонять такие обьемы через Web - нереально.
- Слишком сложная логика переходов между формами. Конечно, это можно попытаться сделать мастером, однако заполнить скажем триста тысяч классифкаторов в мастере невозможно и за год. Однако вполне реально это сделать за неделю, когда все формы висят открытые в MDI-приложении.
В итоге, большая часть управления классикаторами перекочевала в Win-приложение...
В качестве бонуса я покажу тут текст своего одного неплохого контрола, который представляется собою редактируемое дерево c динамически меняющимся контекстным меню. Этот мой редактор классификаторов имеет больше десяток форм, использующих этот контрол, некоторое их которых вы видите выше. Этот контрол я написал не то в 2001-м, не то в 2002-году, когда только появился седьмой бейсик. Этот контрол основан на довольно интресной технике работы с делегатами, похожую технику я использовал здесь.
Вот собственно модуль контрола:
00001: <Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()> _ 00002: Partial Class Ed_TreeViewTest 00003: Inherits System.Windows.Forms.Form 00004: 00005: 'Form overrides dispose to clean up the component list. 00006: <System.Diagnostics.DebuggerNonUserCode()> _ 00007: Protected Overrides Sub Dispose(ByVal disposing As Boolean) 00008: If disposing AndAlso components IsNot Nothing Then 00009: components.Dispose() 00010: End If 00011: MyBase.Dispose(disposing) 00012: End Sub 00013: 00014: 'Required by the Windows Form Designer 00015: Private components As System.ComponentModel.IContainer 00016: 00017: 'NOTE: The following procedure is required by the Windows Form Designer 00018: 'It can be modified using the Windows Form Designer. 00019: 'Do not modify it using the code editor. 00020: <System.Diagnostics.DebuggerStepThrough()> _ 00021: Private Sub InitializeComponent() 00022: Me.components = New System.ComponentModel.Container 00023: Me.SplitContainer1 = New System.Windows.Forms.SplitContainer 00024: Me.Ed_TreeView1 = New Global.Ed_TreeView.Ed_TreeView 00025: Me.ContextMenuStrip1 = New System.Windows.Forms.ContextMenuStrip(Me.components) 00026: Me.ToolStripMenuItem2 = New System.Windows.Forms.ToolStripMenuItem 00027: Me.Label1 = New System.Windows.Forms.Label 00028: Me.SplitContainer1.Panel1.SuspendLayout() 00029: Me.SplitContainer1.Panel2.SuspendLayout() 00030: Me.SplitContainer1.SuspendLayout() 00031: Me.ContextMenuStrip1.SuspendLayout() 00032: Me.SuspendLayout() 00033: ' 00034: 'SplitContainer1 00035: ' 00036: Me.SplitContainer1.Dock = System.Windows.Forms.DockStyle.Fill 00037: Me.SplitContainer1.FixedPanel = System.Windows.Forms.FixedPanel.Panel2 00038: Me.SplitContainer1.Location = New System.Drawing.Point(0, 0) 00039: Me.SplitContainer1.Name = "SplitContainer1" 00040: Me.SplitContainer1.Orientation = System.Windows.Forms.Orientation.Horizontal 00041: ' 00042: 'SplitContainer1.Panel1 00043: ' 00044: Me.SplitContainer1.Panel1.Controls.Add(Me.Ed_TreeView1) 00045: ' 00046: 'SplitContainer1.Panel2 00047: ' 00048: Me.SplitContainer1.Panel2.Controls.Add(Me.Label1) 00049: Me.SplitContainer1.Size = New System.Drawing.Size(292, 273) 00050: Me.SplitContainer1.SplitterDistance = 240 00051: Me.SplitContainer1.TabIndex = 0 00052: ' 00053: 'Ed_TreeView1 00054: ' 00055: Me.Ed_TreeView1.BackColor = System.Drawing.SystemColors.Control 00056: Me.Ed_TreeView1.ContextMenuStrip = Me.ContextMenuStrip1 00057: Me.Ed_TreeView1.Dock = System.Windows.Forms.DockStyle.Fill 00058: Me.Ed_TreeView1.Ed_Level = 1 00059: Me.Ed_TreeView1.Ed_NodeAdd_Delegate_AddressOf = Nothing 00060: Me.Ed_TreeView1.Location = New System.Drawing.Point(0, 0) 00061: Me.Ed_TreeView1.Name = "Ed_TreeView1" 00062: Me.Ed_TreeView1.Size = New System.Drawing.Size(292, 240) 00063: Me.Ed_TreeView1.TabIndex = 0 00064: ' 00065: 'ContextMenuStrip1 00066: ' 00067: Me.ContextMenuStrip1.Items.AddRange(New System.Windows.Forms.ToolStripItem() {Me.ToolStripMenuItem2}) 00068: Me.ContextMenuStrip1.Name = "ContextMenuStrip1" 00069: Me.ContextMenuStrip1.Size = New System.Drawing.Size(92, 26) 00070: ' 00071: 'ToolStripMenuItem2 00072: ' 00073: Me.ToolStripMenuItem2.Name = "ToolStripMenuItem2" 00074: Me.ToolStripMenuItem2.Size = New System.Drawing.Size(91, 22) 00075: Me.ToolStripMenuItem2.Text = "1" 00076: ' 00077: 'Label1 00078: ' 00079: Me.Label1.AutoSize = True 00080: Me.Label1.Dock = System.Windows.Forms.DockStyle.Fill 00081: Me.Label1.Location = New System.Drawing.Point(0, 0) 00082: Me.Label1.Name = "Label1" 00083: Me.Label1.Size = New System.Drawing.Size(39, 13) 00084: Me.Label1.TabIndex = 0 00085: Me.Label1.Text = "Label1" 00086: ' 00087: 'Ed_TreeViewTest 00088: ' 00089: Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!) 00090: Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font 00091: Me.ClientSize = New System.Drawing.Size(292, 273) 00092: Me.Controls.Add(Me.SplitContainer1) 00093: Me.Name = "Ed_TreeViewTest" 00094: Me.Text = "Form1" 00095: Me.SplitContainer1.Panel1.ResumeLayout(False) 00096: Me.SplitContainer1.Panel2.ResumeLayout(False) 00097: Me.SplitContainer1.Panel2.PerformLayout() 00098: Me.SplitContainer1.ResumeLayout(False) 00099: Me.ContextMenuStrip1.ResumeLayout(False) 00100: Me.ResumeLayout(False) 00101: 00102: End Sub 00103: Friend WithEvents SplitContainer1 As System.Windows.Forms.SplitContainer 00104: Friend WithEvents Label1 As System.Windows.Forms.Label 00105: Friend WithEvents Ed_TreeView1 As Ed_TreeView 00106: Friend WithEvents ContextMenuStrip1 As System.Windows.Forms.ContextMenuStrip 00107: Friend WithEvents ToolStripMenuItem2 As System.Windows.Forms.ToolStripMenuItem 00108: End Class 00001: 'Этот контрол с деревом позволяет редактировать по месту узлы в заданном уровне 00002: 'Контрол выдает события - изменения узла, выбора, удаления, добавления (надо задать делегат, возвращающий ключ в базе) 00003: 'Контрол добавляет к заданому извне контекстному меню свое собственное на заданный уровень для выполнения - удаления, добавления и изменения 00004: 'Дерево в контрол загружается извне 00005: 00006: Imports System.ComponentModel 00007: Imports System.Windows.Forms 00008: 00009: <Description("Дерево с редактированием узлов в заданном уровне")> _ 00010: Public Class Ed_TreeView 00011: 00012: <Description("Cобытие выбора узла"), Category("Ed")> _ 00013: Public Event Ed_NodeSelected(ByVal SelectedKey As String, ByVal SelectedText As String) 00014: <Description("Cобытие удаления узла"), Category("Ed")> _ 00015: Public Event Ed_NodeRemove(ByVal SelectedKey As String, ByVal SelectedText As String) 00016: <Description("Cобытие изменения текста узла"), Category("Ed")> _ 00017: Public Event Ed_NodeChanged(ByVal SelectedKey As String, ByVal NewValue As String) 00018: 'Этот делегат должен вернуть реальный ключ, присвоенный добавленному узлу в базе 00019: Public Delegate Function Ed_NodeAdd_Delegate(ByVal ParentNodeKey As String, ByVal NewValue As String) As String 00020: 00021: Dim a_Ed_NodeAdd_Delegate_Addr As Ed_NodeAdd_Delegate 00022: <Description("Этот делегат должен вернуть реальный ключ, присвоенный добавленному узлу в базе"), Category("Ed")> _ 00023: Public Property Ed_NodeAdd_Delegate_AddressOf() As Ed_NodeAdd_Delegate 00024: Get 00025: Ed_NodeAdd_Delegate_AddressOf = a_Ed_NodeAdd_Delegate_Addr 00026: End Get 00027: Set(ByVal value As Ed_NodeAdd_Delegate) 00028: a_Ed_NodeAdd_Delegate_Addr = value 00029: End Set 00030: End Property 00031: 00032: Dim a_Ed_Level As Integer 00033: <Description("Уровень в дереве, который подлежит редактированию"), Category("Ed")> _ 00034: Public Property Ed_Level() As Integer 00035: Get 00036: Ed_Level = a_Ed_Level 00037: End Get 00038: Set(ByVal value As Integer) 00039: a_Ed_Level = value 00040: End Set 00041: End Property 00042: 00043: Dim a_Ed_SelectedKey As String 00044: <Description("Tекущий выбранный юзером ключ в дереве"), Category("Ed")> _ 00045: Public ReadOnly Property Ed_SelectedKey() As String 00046: Get 00047: Ed_SelectedKey = a_Ed_SelectedKey 00048: End Get 00049: End Property 00050: 00051: Dim a_Ed_SelectedText As String 00052: <Description("Tекущий выбранный юзером текст в дереве"), Category("Ed")> _ 00053: Public ReadOnly Property Ed_SelectedText() As String 00054: Get 00055: Ed_SelectedText = a_Ed_SelectedText 00056: End Get 00057: End Property 00058: 00059: <Description("Ссылка на дерево в этом контроле - для внешнего заполнения"), Category("Ed")> _ 00060: Public ReadOnly Property TreeView() As TreeView 00061: Get 00062: TreeView = TreeView1 00063: End Get 00064: End Property 00065: 00066: Public Delegate Sub DoClick() 00067: Dim a_ExternalContextMenu As ContextMenuStrip = New Windows.Forms.ContextMenuStrip 00068: Dim a_InternalContextMenu As ContextMenuStrip = New Windows.Forms.ContextMenuStrip 00069: Private Sub Ed_TreeView_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load 00070: a_ExternalContextMenu = Me.ContextMenuStrip 00071: a_InternalContextMenu = Label1.ContextMenuStrip 00072: If Not (Me.ContextMenuStrip Is Nothing) Then 00073: For Each x As ToolStripMenuItem In Me.ContextMenuStrip.Items 00074: Dim Y As New ToolStripMenuItem 00075: Y.Text = x.Text 00076: Y.Image = x.Image 00077: Dim Z As DoClick = AddressOf x.PerformClick 00078: Dim A As MyEventHandler = New MyEventHandler(Z, Y) 00079: 'AddHandler Y.Click, AddressOf ExternalMenuClick 00080: AddHandler Y.Click, AddressOf A.ExternalMenuClick 00081: a_InternalContextMenu.Items.Add(Y) 00082: Next 00083: End If 00084: If a_Ed_Level = 0 Then MsgBox("Не задан уровень редактирования дерева") 00085: End Sub 00086: 00087: 'выбранный правой кнопкой мышки узел сразу становится SELECTED 00088: Private Sub TreeView1_NodeMouseClick(ByVal sender As Object, ByVal e As System.Windows.Forms.TreeNodeMouseClickEventArgs) Handles TreeView1.NodeMouseClick 00089: TreeView1.SelectedNode = e.Node 00090: If e.Button = Windows.Forms.MouseButtons.Right Then 00091: If e.Node.Level = a_Ed_Level Then 00092: 'добавим сюда контекстное меню редактирования дерева 00093: TreeView1.ContextMenuStrip = Nothing 00094: TreeView1.ContextMenuStrip = a_InternalContextMenu 00095: Else 00096: TreeView1.ContextMenuStrip = Nothing 00097: TreeView1.ContextMenuStrip = a_ExternalContextMenu 00098: End If 00099: End If 00100: End Sub 00101: 00102: 'узел выбран - выходим к юзеру с заблокированным курсором для удобства 00103: Private Sub TreeView1_AfterSelect(ByVal sender As Object, ByVal e As System.Windows.Forms.TreeViewEventArgs) Handles TreeView1.AfterSelect 00104: Dim O As Cursor = Me.Cursor 00105: Me.Cursor = Cursors.WaitCursor 00106: a_Ed_SelectedKey = e.Node.Name 00107: a_Ed_SelectedText = e.Node.Text 00108: RaiseEvent Ed_NodeSelected(e.Node.Name, e.Node.Text) 00109: If e.Node.Level = a_Ed_Level Then 00110: For Each X As TreeNode In TreeView1.Nodes 00111: Call Recurs(X) 00112: Next 00113: TreeView1.SelectedNode.ForeColor = Drawing.Color.Blue 00114: End If 00115: Me.Cursor = O 00116: End Sub 00117: 00118: 'Рекурсивный обход всех нодов и сброс выделения цветом в текущем рабочем уровне 00119: Private Sub Recurs(ByVal x As TreeNode) 00120: If x.Level = a_Ed_Level Then x.ForeColor = Drawing.Color.Black 00121: If x.Level < a_Ed_Level Then 00122: 'продолжаем углубляться в глубину 00123: If Not (x.Nodes Is Nothing) Then 00124: For Each Y As TreeNode In x.Nodes 00125: Recurs(Y) 00126: Next 00127: End If 00128: End If 00129: 00130: End Sub 00131: 00132: 'Удаление узла 00133: Private Sub УдалитьToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles УдалитьToolStripMenuItem.Click 00134: If TreeView1.SelectedNode.Level = a_Ed_Level Then 00135: Dim O As Cursor = Me.Cursor 00136: Me.Cursor = Cursors.WaitCursor 00137: TreeView1.SelectedNode.Remove() 00138: RaiseEvent Ed_NodeRemove(a_Ed_SelectedKey, a_Ed_SelectedText) 00139: Me.Cursor = O 00140: End If 00141: End Sub 00142: 00143: #Region "Изменение узла" 00144: 00145: Private Sub ИзменитьToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ИзменитьToolStripMenuItem.Click 00146: If TreeView1.SelectedNode.Level = a_Ed_Level Then 00147: Dim T As New Windows.Forms.TextBox 00148: T.Text = TreeView1.SelectedNode.Text 00149: T.Top = TreeView1.SelectedNode.Bounds.Top 00150: T.Left = TreeView1.SelectedNode.Bounds.Left 00151: T.Width = Me.Width - 100 00152: T.Cursor = Cursors.IBeam 00153: Me.Controls.Add(T) 00154: T.BringToFront() 00155: T.Focus() 00156: AddHandler T.Leave, AddressOf NodeChange 00157: AddHandler T.KeyPress, AddressOf NodeEnter 00158: AddHandler T.TextChanged, AddressOf TxtChange 00159: TreeView1.Enabled = False 00160: End If 00161: End Sub 00162: 00163: Private Sub TxtChange(ByVal sender As System.Object, ByVal e As System.EventArgs) 00164: Dim T As TextBox = CType(sender, TextBox) 00165: T.ForeColor = Drawing.Color.Red 00166: End Sub 00167: 00168: Private Sub NodeChange(ByVal sender As System.Object, ByVal e As System.EventArgs) 00169: Dim O As Cursor = Me.Cursor 00170: Me.Cursor = Cursors.WaitCursor 00171: Dim T As TextBox = CType(sender, TextBox) 00172: Dim NewText As String = T.Text 00173: RemoveHandler T.Leave, AddressOf NodeChange 00174: RemoveHandler T.Enter, AddressOf NodeChange 00175: RemoveHandler T.TextChanged, AddressOf TxtChange 00176: Me.Controls.Remove(T) 00177: T = Nothing 00178: TreeView1.SelectedNode.Text = NewText 00179: a_Ed_SelectedText = NewText 00180: RaiseEvent Ed_NodeChanged(a_Ed_SelectedKey, NewText) 00181: Me.Cursor = O 00182: TreeView1.Enabled = True 00183: End Sub 00184: 00185: Private Sub NodeEnter(ByVal sender As System.Object, ByVal e As System.EventArgs) 00186: If CType(e, Windows.Forms.KeyPressEventArgs).KeyChar = vbCr Then 00187: Call NodeChange(sender, e) 00188: End If 00189: End Sub 00190: 00191: #End Region 00192: 00193: #Region "Добавление узла" 00194: 00195: Private Sub ДобавитьToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ДобавитьToolStripMenuItem.Click 00196: If TreeView1.SelectedNode.Level = a_Ed_Level Then 00197: Dim O As Cursor = Me.Cursor 00198: Me.Cursor = Cursors.WaitCursor 00199: Dim X As TreeNode = TreeView1.SelectedNode 00200: Dim Y As TreeNode = TreeView1.SelectedNode.Parent 00201: Y.Nodes.Add(Y.Name & "," & Guid.NewGuid.ToString, "") 00202: Dim i As Integer 00203: For i = Y.Nodes.Count - 1 To 1 Step -1 00204: 'раздвижка - перегон всех узлов вниз - это удобно 00205: Y.Nodes(i).Name = Y.Nodes(i - 1).Name 00206: Y.Nodes(i).Text = Y.Nodes(i - 1).Text 00207: If Y.Nodes(i) Is X Then 00208: Exit For 00209: End If 00210: Next 00211: Y.Nodes(i).Name = Y.Name & Guid.NewGuid.ToString 00212: Y.Nodes(i).Text = "" 00213: a_Ed_SelectedText = "" 00214: 'и переход в редактирование 00215: Dim T As New Windows.Forms.TextBox 00216: T.Text = TreeView1.SelectedNode.Text 00217: T.Top = TreeView1.SelectedNode.Bounds.Top 00218: T.Left = TreeView1.SelectedNode.Bounds.Left 00219: T.Width = Me.Width - 100 00220: T.Cursor = Cursors.IBeam 00221: Me.Controls.Add(T) 00222: T.BringToFront() 00223: T.Focus() 00224: AddHandler T.Leave, AddressOf NodeAdd 00225: AddHandler T.KeyPress, AddressOf NodeAddEnter 00226: Me.Cursor = O 00227: TreeView1.Enabled = False 00228: End If 00229: End Sub 00230: 00231: Private Sub NodeAdd(ByVal sender As System.Object, ByVal e As System.EventArgs) 00232: Dim O As Cursor = Me.Cursor 00233: Me.Cursor = Cursors.WaitCursor 00234: Dim T As TextBox = CType(sender, TextBox) 00235: Dim NewText As String = T.Text 00236: RemoveHandler T.Leave, AddressOf NodeAdd 00237: RemoveHandler T.Enter, AddressOf TxtChange 00238: Me.Controls.Remove(T) 00239: T = Nothing 00240: TreeView1.SelectedNode.Text = NewText 00241: a_Ed_SelectedText = NewText 00242: TreeView1.SelectedNode.Name = a_Ed_NodeAdd_Delegate_Addr(TreeView1.SelectedNode.Parent.Name, NewText) 00243: a_Ed_SelectedKey = TreeView1.SelectedNode.Name 00244: RaiseEvent Ed_NodeSelected(a_Ed_SelectedKey, a_Ed_SelectedText) 00245: Me.Cursor = O 00246: TreeView1.Enabled = True 00247: End Sub 00248: 00249: Private Sub NodeAddEnter(ByVal sender As System.Object, ByVal e As System.EventArgs) 00250: If CType(e, Windows.Forms.KeyPressEventArgs).KeyChar = vbCr Then 00251: Call NodeAdd(sender, e) 00252: End If 00253: End Sub 00254: 00255: #End Region 00256: 00257: Private Sub CM1_Opening(ByVal sender As Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles CM1.Opening 00258: e.Cancel = False 00259: End Sub 00260: 00261: End Class 00262: 00263: Public Class MyEventHandler 00264: Dim X As Ed_TreeView.DoClick 00265: Dim y As ToolStripMenuItem 00266: 00267: Public Sub New(ByVal PerformClick As Ed_TreeView.DoClick, ByVal sender As ToolStripMenuItem) 00268: X = PerformClick 00269: y = sender 00270: End Sub 00271: 00272: Public Sub ExternalMenuClick(ByVal sender As Object, ByVal e As System.EventArgs) 00273: X.Invoke() 00274: End Sub 00275: 00276: End Class
В библиотеке, этот класс у меня лежит прямо вместе с тестом, который выглядит так:
Как видите, мой контрол добавляет на заданный уровень дерева контекстное меню (в дополнение к заданному извне) и позволяет по месту редактировать дерево, обновляя его из базы. Желающие могут найти в моем коде несколько интересных моментов, начиная от элементарных - рекурсии, кончая техникой работы с захватом адресов переходов для контекстного меню.
Ну и наконец, тест теста - естественно при компиляции - укажите для вызова иную точку входа в библиотеку, чем просто для использования контрола.
00001: <Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()> _ 00002: Partial Class Ed_TreeView 00003: Inherits System.Windows.Forms.UserControl 00004: 00005: 'UserControl overrides dispose to clean up the component list. 00006: <System.Diagnostics.DebuggerNonUserCode()> _ 00007: Protected Overrides Sub Dispose(ByVal disposing As Boolean) 00008: If disposing AndAlso components IsNot Nothing Then 00009: components.Dispose() 00010: End If 00011: MyBase.Dispose(disposing) 00012: End Sub 00013: 00014: 'Required by the Windows Form Designer 00015: Private components As System.ComponentModel.IContainer 00016: 00017: 'NOTE: The following procedure is required by the Windows Form Designer 00018: 'It can be modified using the Windows Form Designer. 00019: 'Do not modify it using the code editor. 00020: <System.Diagnostics.DebuggerStepThrough()> _ 00021: Private Sub InitializeComponent() 00022: Me.components = New System.ComponentModel.Container 00023: Me.TreeView1 = New System.Windows.Forms.TreeView 00024: Me.CM1 = New System.Windows.Forms.ContextMenuStrip(Me.components) 00025: Me.ДобавитьToolStripMenuItem = New System.Windows.Forms.ToolStripMenuItem 00026: Me.УдалитьToolStripMenuItem = New System.Windows.Forms.ToolStripMenuItem 00027: Me.ИзменитьToolStripMenuItem = New System.Windows.Forms.ToolStripMenuItem 00028: Me.ToolStripSeparator1 = New System.Windows.Forms.ToolStripSeparator 00029: Me.Label1 = New System.Windows.Forms.Label 00030: Me.CM1.SuspendLayout() 00031: Me.SuspendLayout() 00032: ' 00033: 'TreeView1 00034: ' 00035: Me.TreeView1.Dock = System.Windows.Forms.DockStyle.Fill 00036: Me.TreeView1.Location = New System.Drawing.Point(0, 0) 00037: Me.TreeView1.Name = "TreeView1" 00038: Me.TreeView1.Size = New System.Drawing.Size(150, 150) 00039: Me.TreeView1.TabIndex = 0 00040: ' 00041: 'CM1 00042: ' 00043: Me.CM1.Items.AddRange(New System.Windows.Forms.ToolStripItem() {Me.ДобавитьToolStripMenuItem, Me.УдалитьToolStripMenuItem, Me.ИзменитьToolStripMenuItem, Me.ToolStripSeparator1}) 00044: Me.CM1.Name = "CM1" 00045: Me.CM1.Size = New System.Drawing.Size(136, 76) 00046: ' 00047: 'ДобавитьToolStripMenuItem 00048: ' 00049: Me.ДобавитьToolStripMenuItem.Name = "ДобавитьToolStripMenuItem" 00050: Me.ДобавитьToolStripMenuItem.Size = New System.Drawing.Size(135, 22) 00051: Me.ДобавитьToolStripMenuItem.Text = "Добавить" 00052: ' 00053: 'УдалитьToolStripMenuItem 00054: ' 00055: Me.УдалитьToolStripMenuItem.Name = "УдалитьToolStripMenuItem" 00056: Me.УдалитьToolStripMenuItem.Size = New System.Drawing.Size(135, 22) 00057: Me.УдалитьToolStripMenuItem.Text = "Удалить" 00058: ' 00059: 'ИзменитьToolStripMenuItem 00060: ' 00061: Me.ИзменитьToolStripMenuItem.Name = "ИзменитьToolStripMenuItem" 00062: Me.ИзменитьToolStripMenuItem.Size = New System.Drawing.Size(135, 22) 00063: Me.ИзменитьToolStripMenuItem.Text = "Изменить" 00064: ' 00065: 'ToolStripSeparator1 00066: ' 00067: Me.ToolStripSeparator1.Name = "ToolStripSeparator1" 00068: Me.ToolStripSeparator1.Size = New System.Drawing.Size(132, 6) 00069: ' 00070: 'Label1 00071: ' 00072: Me.Label1.AutoSize = True 00073: Me.Label1.ContextMenuStrip = Me.CM1 00074: Me.Label1.Location = New System.Drawing.Point(64, 134) 00075: Me.Label1.Name = "Label1" 00076: Me.Label1.Size = New System.Drawing.Size(0, 13) 00077: Me.Label1.TabIndex = 1 00078: ' 00079: 'Ed_TreeView 00080: ' 00081: Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!) 00082: Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font 00083: Me.Controls.Add(Me.TreeView1) 00084: Me.Controls.Add(Me.Label1) 00085: Me.Name = "Ed_TreeView" 00086: Me.CM1.ResumeLayout(False) 00087: Me.ResumeLayout(False) 00088: Me.PerformLayout() 00089: 00090: End Sub 00091: Friend WithEvents TreeView1 As System.Windows.Forms.TreeView 00092: Friend WithEvents CM1 As System.Windows.Forms.ContextMenuStrip 00093: Friend WithEvents ДобавитьToolStripMenuItem As System.Windows.Forms.ToolStripMenuItem 00094: Friend WithEvents УдалитьToolStripMenuItem As System.Windows.Forms.ToolStripMenuItem 00095: Friend WithEvents ИзменитьToolStripMenuItem As System.Windows.Forms.ToolStripMenuItem 00096: Friend WithEvents ToolStripSeparator1 As System.Windows.Forms.ToolStripSeparator 00097: Friend WithEvents Label1 As System.Windows.Forms.Label 00098: 00099: End Class 00001: Public Class Ed_TreeViewTest 00002: 00003: Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load 00004: Ed_TreeView1.TreeView1.Nodes.Add("root", "root") 00005: Ed_TreeView1.TreeView1.Nodes(0).Nodes.Add("1", "1") 00006: Ed_TreeView1.TreeView1.Nodes(0).LastNode.Nodes.Add("a", "a") 00007: Ed_TreeView1.TreeView1.Nodes(0).LastNode.Nodes.Add("b", "b") 00008: Ed_TreeView1.TreeView1.Nodes(0).Nodes.Add("2", "2") 00009: Ed_TreeView1.TreeView1.Nodes(0).Nodes.Add("3", "3") 00010: Ed_TreeView1.Ed_NodeAdd_Delegate_AddressOf = AddressOf Ed_NodeAdd 00011: End Sub 00012: 00013: Private Sub Ed_TreeView1_Ed_NodeChanged(ByVal SelectedKey As String, ByVal NewValue As String) Handles Ed_TreeView1.Ed_NodeChanged 00014: ' 00015: End Sub 00016: 00017: Private Sub Ed_TreeView1_Ed_NodeRemove(ByVal SelectedKey As String, ByVal SelectedText As String) Handles Ed_TreeView1.Ed_NodeRemove 00018: ' 00019: End Sub 00020: 00021: Private Sub Ed_TreeView1_Ed_NodeSelected(ByVal SelectedKey As String, ByVal SelectedText As String) Handles Ed_TreeView1.Ed_NodeSelected 00022: Label1.Text = SelectedKey 00023: End Sub 00024: 00025: Public Function Ed_NodeAdd(ByVal ParentKey As String, ByVal NewText As String) As String 00026: Return Guid.NewGuid.ToString 00027: End Function 00028: 00029: Private Sub ToolStripMenuItem2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ToolStripMenuItem2.Click 00030: ' 00031: End Sub 00032: 00033: 00034: End Class
Ну и в заключение этого топика я расскажу, как надо правильно заполнять деревья за одно обращение в базу - ну по крайней мере я делаю именно так уже не один год.
Конечно, деревья можно заполнять и в реляционной технике, те заполнив каждый узел, далее вытащить из базы все его подчиненные узлы. Но можете себе представить, какой жуткий SQL-трафик порождает это отстойная шестерочная техника.
Поэтому в .NET такие жутковатые шестерочные техники надо избегать всеми силами. Здесь ведь можно вытащить всю иерархию данных за одно обращение к SQL:
и загрузить ее в дерево буквально в три строки:
Что привлекательно в .NET, что Win-деревья и Web-деревья заполняются АБСОЛЮТНО аналогично.
|