Alessandro Badalin


Il blog di Alessandro Badalin
Blogs Amici
Archivio Posts
Anno 2008

Anno 2007

DataTree ovvero controllo TreeView collegato ad un DataSet

DataTree ovvero Controllo TreeView collegato ad un DataSet

E rieccomi, dopo lunga assenza, a pubblicare un nuovo articolo relativo ad un controllo che ho recentemente realizzato (ancora molto ‘grezzo’ ma funzionante!)

La necessità è quella di avere un controllo TreeView che venga popolato da un DataSet e che ne gestisca l’inserimento, eliminazione o modifica degli elementi (nodi) in esso contenuti.

Vado a riportare un esempio:

Date tre tabelle (Articoli, Famiglie, Tipologie) tra loro relazionate, vogliamo visualizzare su un TreeView il nostro DataSet per ottenere un risultato di questo tipo:

Il controllo che ho realizzato che ovviamente eredita dal controllo TreeView, accetta un DataSet come sorgente dati contentente da 1 a 3 DataTable, per ciascun DataTable contenuto, dovremmo fornire passando degli array, le informazioni relative al nome dal DataTable, alla chiave primaria di ogni DataTable, nonché alla chiave di relazione con la tabella di livello superiore.

Tramite un Context menu attivato alla pressione del tasto destro del mouse in corrispondenza di uno dei nodi del DataTree, sarà possibile aggiungere un nuovo nodo, modificarne uno esistente, spostarlo in su o in giù rispetto alla posizione attuale.

Sintesi di Metodi e Proprietà personalizzate

PROPRIETA’

DESCRIZIONE

 

 

DataSetOrigin

Rappresenta il DataSet di origine passato in reference al controllo  a runtime/designtime

Tablename()

Array contenente il nome dei DataTable (max 3)

IDField()

Array contenente il nome dei campi chiave delle tabelle

DescriptionField()

Array contenente il nome dei campi descrizione (visualizzati come label dei nodi)

ParentField()

Array contenente il nome dei campi chiave della tabella parent di 1° livello

GrandParentField()

Array contenente il nome dei campi chiave della tabella parent di 2° livello

 

 

METODI

DESCRIZIONE

 

 

MoveUp (CurNode)

Sposta verso l’alto il nodo selezionato

MoveDown (CurNode)

Sposta verso il basso il nodo selezionato

DeleteNode (CurNode)

Elimina il nodo selezionato

AddNode (CurNode)

Aggiunge un nuovo nodo figlio rispetto al nodo selezionato

AddRootNode

Aggiunge un Nodo al primo livello (root)

ShowData

Genera e visualizza i nodi

 

 

EVENTI

DESCRIZIONE

 

 

BeforeDeleteNode

Evento generato prima dell’eliminazione di un nodo, consente di controllare l’eliminazione dei nodi.

 

Esempio di utilizzo:

Una volta compilato e generato il controllo (allego sotto il codice sorgente),  aggiungerlo ai controlli della nostra soluzione.

Creiamo un DataSet contenente 3 DataTable (che chiameremo Ds):

1.       FAMIGLIE

Campo

Tipo

CdFamiglia

Decimal

DsFamiglia

Text

 

2.       TIPOLOGIE

Campo

Tipo

CdFamiglia

Decimal

CdTipologia

Decimal

DsTipologia

Text

3.       ARTICOLI

Campo

Tipo

CdFamiglia

Decimal

CdTipologia

Decimal

CdArticolo

Decimal

DsArticolo

Text

 

 

 

 

 


Trasciniamo il controllo nella nostra Form e denominiamolo TreeArticoli

A questo punto inizializziamo il nostro controllo impostando le relative proprietà:

        With Me.TreeArticoli

            .DataSetOrigin = DS

            .TableName(0) = "Famiglie"

            .IDField(0) = "CdFamiglia"

            .DescriptionField(0) = "DsFamiglia"

            .TableName(1) = "Tipologie"

            .ParentField(0) = "CdFamiglia"

            .IDField(1) = "CdTipologia"

            .DescriptionField(1) = "DsTipologia"

            .TableName(2) = "Articoli"

            .GrandParentField = "CdFamiglia"

            .ParentField(1) = "CdTipologia"

            .IDField(2) = "CdArticolo"

            .DescriptionField(2) = "DsArticolo"

            .ShowData()

        End With

Mandando in esecuzione la nostra applicazione vedremo che il nostro DataTree si popolerà automaticamente con i dati provenienti dal DataSet passato.

Ora attraverso il metodo AddRootNode potremo aggiungere nodi di primo livello (root), la pressione del tasto destro del mouse in corrispondenza di un nodo esistente mi consentirà di spostarlo, eliminarlo o aggiungere un nodo figlio rispetto a quello selezionato.

Il controllo è ancora in fase di beta testing, per cui non ne garantisco il perfetto funzionamento, consigli o segnalazioni sono ben accetti.

ED ECCO IL CODICE RELATIVO AL CONTROLLO

Imports System.ComponentModel

 

Public NotInheritable Class DataTree

 

    Inherits System.Windows.Forms.TreeView

 

#Region "Dichiarazioni"

 

    Dim Ds As DataSet

    Dim StrTableName(2) As String

    Dim StrIDField(2) As String

    Dim StrFieldDescription(2) As String

    Dim StrFieldParent(1) As String

    Dim StrFieldGrandParent As String

    Dim StrSeparatore As String

 

#End Region

 

#Region "Proprietà"

 

    <Bindable(True), CategoryAttribute("DataBinding"), _

DescriptionAttribute("Imposta/Ottiene il Dataset contenente le tabelle collegate al controllo")> _

Public Property DataSetOrigin() As DataSet

 

        ' Restituisce/Imposta il Dataset collegato al treeview

 

        Get

            Return Ds

        End Get

 

        Set(ByVal value As DataSet)

            Ds = value

        End Set

 

    End Property

 

    <Bindable(True), CategoryAttribute("DataBinding"), _

DescriptionAttribute("Imposta la denominazione delle tabelle collegate al controllo")> _

    Public Property TableName() As String()

 

        ' Restituisce/Imposta il nome delle tabelle contenente i dati

        ' da collegare al TreeView

 

        Get

            Return StrTableName

        End Get

 

        Set(ByVal value As String())

            StrTableName = value

        End Set

 

    End Property

 

    <Bindable(True), CategoryAttribute("DataBinding"), _

DescriptionAttribute("Imposta il nome del campo chiave relativo alle tabelle")> _

    Public Property IDField() As String()

 

        ' Restituisce/Imposta il nome del campo ID

 

        Get

            Return StrIDField

        End Get

 

        Set(ByVal value As String())

            StrIDField = value

        End Set

 

    End Property

 

    <Bindable(True), CategoryAttribute("DataBinding"), _

DescriptionAttribute("Imposta il nome del campo descrittivo relativo alle tabelle")> _

    Public Property DescriptionField() As String()

 

        ' Restituisce/Imposta il nome del campo Descrittivo

 

        Get

            Return StrFieldDescription

        End Get

 

        Set(ByVal value As String())

            StrFieldDescription = value

        End Set

 

    End Property

 

    <Bindable(True), CategoryAttribute("DataBinding"), _

DescriptionAttribute("Imposta il nome del campo padre relativo alle tabella a livello superiore")> _

    Public Property ParentField() As String()

 

        ' Restituisce/Imposta il nome del campo Chiave in riferimento al compo ID

        ' della tabella padre

 

        Get

            Return StrFieldParent

        End Get

 

        Set(ByVal value As String())

            StrFieldParent = value

        End Set

 

    End Property

 

    <Bindable(True), CategoryAttribute("DataBinding"), _

DescriptionAttribute("Imposta il nome del campo padre relativo alle tabella a livello superiore")> _

Public Property GrandParentField() As String

 

        ' Restituisce/Imposta il nome del campo Chiave in riferimento al compo ID

        ' della tabella padre

 

        Get

            Return StrFieldGrandParent

        End Get

 

        Set(ByVal value As String)

            StrFieldGrandParent = value

        End Set

 

    End Property

 

#End Region

 

#Region "Metodi di classe"

 

    Public Sub MoveUp(ByVal Node As TreeNode)

 

        ' Sposta il nodo selezionato verso l'alto nel treeview

 

        Dim NewIndex As Integer = 0

        Dim NodeClone As TreeNode = Nothing

 

        If node.Parent Is Nothing Then Exit Sub

        Try

 

            If node Is Nothing Then Return

            If node.Index = 0 Then Return

 

            NewIndex = node.Index - 1

            NodeClone = CType(node.Clone(), TreeNode)

            node.Parent.Nodes.Insert(NewIndex, NodeClone)

            node.Parent.Nodes.Remove(node)

            NodeClone.TreeView.SelectedNode = NodeClone

 

        Catch ex As Exception

            Throw New Exception("Errore nello spostamento verso l'alto del nodo")

        End Try

 

    End Sub

 

    Public Sub MoveDown(ByVal Node As TreeNode)

 

        ' Sposta il nodo selezionato verso il basso nel treeview

 

        Dim NewIndex As Integer = 0

        Dim NodeClone As TreeNode = Nothing

        If node.Parent Is Nothing Then Exit Sub

 

        Try

 

            If node Is Nothing Then Return

            NewIndex = node.Index + 2

 

            If NewIndex > node.Parent.Nodes.Count Then Return

            NodeClone = CType(node.Clone(), TreeNode)

            node.Parent.Nodes.Insert(NewIndex, NodeClone)

            node.Parent.Nodes.Remove(node)

            NodeClone.TreeView.SelectedNode = NodeClone

 

        Catch ex As Exception

            Throw New Exception("Errore nello spostamento verso il basso del nodo")

        End Try

        Return

 

    End Sub

 

    Public Sub DeleteNode(ByVal StateNode As TreeNode)

 

        ' Elimina il nodo selezionato

        Try

            If StateNode Is Nothing Then Return

 

            ' Scatena l'evento BeforeDeleteNode intercettabile per gestire un'eventuale

            ' annullamento dell'eliminazione

 

            Dim IsCancelled As Boolean = False

            Dim Action As TreeViewAction = TreeViewAction.Unknown

            RaiseEvent BeforeDeleteNode(Me, New TreeViewCancelEventArgs(StateNode, IsCancelled, Action))

            If IsCancelled Then Exit Sub

 

            Dim row As DataRow = CType(StateNode.Tag, DataRow)

            If Not IsNothing(row) Then

 

                ' Elimina tutti i nodi figli

                Dim ParentNodes As TreeNodeCollection = StateNode.Nodes

                For Each ParentNode As TreeNode In ParentNodes

 

                    Dim ChildNodes As TreeNodeCollection = ParentNode.Nodes

                    For Each ChildNode As TreeNode In ChildNodes

                        Dim Childrow As DataRow = CType(ChildNode.Tag, DataRow)

                        If Not IsNothing(Childrow) Then

                            Childrow.Delete()

                        End If

                    Next

 

                    Dim ParentRow As DataRow = CType(ParentNode.Tag, DataRow)

 

                    If Not IsNothing(ParentRow) Then

                        ParentRow.Delete()

                        ParentRow.Delete()

                    End If

                Next

 

                ' Elimina il nodo padre

                row = CType(StateNode.Tag, DataRow)

                If Not IsNothing(row) Then

                    row.Delete()

                End If

 

            End If

 

            StateNode.Remove()

        Catch ex As Exception

            '

        End Try

        Return

 

    End Sub

 

    Public Sub AddNode(ByVal Node As TreeNode)

 

        Dim newNode As TreeNode = Nothing

        Dim ParentRow As DataRow = Nothing

        Dim dt As DataTable = Nothing

        Dim dtAdd As DataTable = Nothing

        Dim newindex As Integer = 0

 

        Me.Cursor = Cursors.WaitCursor

        Try

            ParentRow = CType(Node.Tag, DataRow)

            If ParentRow Is Nothing Then Return

            dt = ParentRow.Table

 

            ' Controlla che non ci troviamo all'ultimo livello gerarchico

 

            If dt.TableName = StrTableName(2) Then

                MsgBox("Impossibile creare ulteriori livelli")

                Exit Sub

            End If

 

            ' Create la nuova riga nel DataTable

 

            If dt.TableName = StrTableName(0) Then

                dtAdd = Ds.Tables(StrTableName(1))

                Dim CurNewRow As DataRow = dtAdd.Rows.Add

                CurNewRow.Item(StrFieldParent(0)) = ParentRow.Item(StrIDField(0))

                ' Trova il primo valore key progressivo

 

                Dim StrFiltro As String

                StrFiltro = StrFieldParent(0) & "=" & ParentRow.Item(StrIDField(0))

 

                CurNewRow.Item(StrFieldDescription(1)) = "Nuovo nodo"

                CurNewRow.Item(StrIDField(1)) = TrovaNuovo(dtAdd, StrIDField(1), StrFiltro)

 

                ' Aggiunge il nodo

 

                newNode = Node.Nodes.Add("Nuovo nodo")

                newNode.Tag = CurNewRow

 

                ' Espande il nodo corrente

 

                Node.Expand()

 

                ' Pone in modalità modifica il nuovo nodo creato

 

                Me.SelectedNode = newNode

                newNode.BeginEdit()

            ElseIf dt.TableName = StrTableName(1) Then

                dtAdd = Ds.Tables(StrTableName(2))

                Dim CurNewRow As DataRow = dtAdd.Rows.Add

 

                CurNewRow.Item(StrFieldGrandParent) = ParentRow.Item(StrFieldParent(0))

                CurNewRow.Item(StrFieldParent(1)) = ParentRow.Item(StrIDField(1))

 

                ' Trova il primo valore key progressivo

                Dim StrFiltro As String

                StrFiltro = StrFieldGrandParent & "=" & ParentRow.Item(StrFieldParent(0)) & " AND " & StrFieldParent(1) & "=" & ParentRow.Item(StrIDField(1))

 

                CurNewRow.Item(StrFieldDescription(2)) = "Nuovo nodo"

                CurNewRow.Item(StrIDField(2)) = TrovaNuovo(dtAdd, StrIDField(2), StrFiltro)

 

                ' Aggiunge il nodo

                newNode = Node.Nodes.Add("Nuovo nodo")

                newNode.Tag = CurNewRow

 

                ' Espande il nodo corrente

                Node.Expand()

 

                ' Pone in modalità modifica il nuovo nodo creato

                Me.SelectedNode = newNode

                newNode.BeginEdit()

            End If

        Catch ex As Exception

            '

        Finally

            Me.Cursor = Cursors.Default

        End Try

 

    End Sub

 

    Public Sub AddRootNode()

 

        Dim newNode As TreeNode = Nothing

        Dim ParentRow As DataRow = Nothing

        Dim dt As DataTable = Nothing

        Dim newindex As Integer = 0

 

        Me.Cursor = Cursors.WaitCursor

        Try

            ' Create la nuova riga nel DataTable

 

            dt = Ds.Tables(StrTableName(0))

            Dim CurNewRow As DataRow = dt.Rows.Add

 

            ' Trova il primo valore key progressivo

 

            CurNewRow.Item(StrIDField(0)) = TrovaNuovo(dt, StrIDField(0), "")

            CurNewRow.Item(StrFieldDescription(0)) = "Nuovo nodo"

 

            ' Aggiunge il nodo

 

            newNode = Me.Nodes.Add("Nuovo nodo")

            newNode.Tag = CurNewRow

 

            ' Pone in modalità modifica il nuovo nodo creato

 

            Me.SelectedNode = newNode

            newNode.BeginEdit()

 

        Catch ex As Exception

            '

        Finally

            Me.Cursor = Cursors.Default

        End Try

 

    End Sub

 

    Public Sub ShowData()

 

        ' Carica i nodi principali

 

        Call LoadRootNodes()

 

    End Sub

 

#End Region

 

#Region "Routine di classe"

 

    Public Sub ShowContextMenu(ByVal tv As TreeView, ByVal menu As ContextMenu)

 

        ' Mostra il menu contestuale

 

        Try

            Dim pt As New Point(tv.SelectedNode.Bounds.Left, tv.SelectedNode.Bounds.Bottom)

 

            menu.Show(tv, pt)

        Catch ex As Exception

            Throw

        End Try

 

    End Sub

 

    Private Sub LoadRootNodes()

 

        ' Carica i nodi principali dalla prima tabella del Dataset

 

        Dim Dt As DataTable = Ds.Tables(StrTableName(0))

 

        For Each CurRow As DataRow In Dt.Rows

            Dim CurNode As TreeNode = Me.Nodes.Add(CurRow.Item(StrFieldDescription(0)))

            CurNode.Tag = CurRow

            LoadParentNodes(CurNode, CurRow.Item(StrIDField(0)))

        Next

 

    End Sub

 

    Private Sub LoadParentNodes(ByVal CurNode As TreeNode, ByVal RootValue As Integer)

 

        ' Carica i nodi figli dalla seconda tabella del Dataset

 

        Dim SelRows As DataRow()

        SelRows = Ds.Tables(StrTableName(1)).Select(StrFieldParent(0) & "=" & RootValue)

 

        For Each CurRow As DataRow In SelRows

            Dim NewNode As TreeNode = CurNode.Nodes.Add(CurRow.Item(StrFieldDescription(1)))

            NewNode.Tag = CurRow

            LoadChildNodes(NewNode, RootValue, CurRow.Item(StrIDField(1)))

        Next

 

    End Sub

 

    Private Sub LoadChildNodes(ByVal CurNode As TreeNode, ByVal RootValue As Integer, ByVal ParentValue As Integer)

 

        ' Carica i nodi figli dalla seconda tabella del Dataset

 

        Dim SelRows As DataRow()

        SelRows = Ds.Tables(StrTableName(2)).Select(StrFieldGrandParent & "=" & RootValue & " AND " & StrFieldParent(1) & "=" & ParentValue)

 

        For Each CurRow As DataRow In SelRows

            If Not IsDBNull(CurRow.Item(StrFieldDescription(2))) Then

                Dim NewNode As TreeNode = CurNode.Nodes.Add(CurRow.Item(StrFieldDescription(2)))

                NewNode.Tag = CurRow

            End If

        Next

 

    End Sub

 

    Public Sub New()

 

        ' Chiamata richiesta da Progettazione Windows Form.

        InitializeComponent()

 

        ' Aggiungere le eventuali istruzioni di inizializzazione dopo la chiamata a InitializeComponent().

 

        For IntCont As Integer = 0 To 2

            StrTableName(IntCont) = ""

            StrIDField(IntCont) = ""

            StrFieldDescription(IntCont) = ""

        Next

 

        For IntCont As Integer = 0 To 1

            StrFieldParent(IntCont) = ""

        Next

 

        StrFieldGrandParent = ""

 

        ' Inizializza il menu contestuale

 

        InizializzaMenu()

 

    End Sub

 

    Private Sub InizializzaMenu()

 

        ' ctxMenu1

 

        With CtxMenu

            .Items.Add("Inserisci", Nothing, New EventHandler(AddressOf InserisciNuovoNodo))

            .Items.Add("Modifica", Nothing, New EventHandler(AddressOf ModificaNodo))

            .Items.Add("Sposta su", Nothing, New EventHandler(AddressOf SpostaSu))

            .Items.Add("Sposta giù", Nothing, New EventHandler(AddressOf SpostaGiu))

            .Items.Add("Elimina", Nothing, New EventHandler(AddressOf EliminaNodo))

        End With

 

    End Sub

 

    Private Function TrovaNuovo(ByVal dt As DataTable, ByVal Key As String, ByVal StrFilter As String) As Integer

 

        ' Trova il primo codice key progressivo successivo

 

        Dim Dv As New DataView

        Dv.Table = dt

        Dv.RowFilter = StrFilter

        Dv.Sort = Key & " DESC"

 

        Try

            Return Integer.Parse(Dv.Item(0).Item(Key)) + 1

        Catch ex As Exception

            Return 1

        End Try

 

    End Function

 

#End Region

 

#Region "Eventi Context menu"

 

    Private Sub InserisciNuovoNodo(ByVal sender As Object, ByVal e As EventArgs)

 

        AddNode(Me.SelectedNode)

 

    End Sub

 

    Private Sub ModificaNodo(ByVal sender As Object, ByVal e As EventArgs)

 

        Me.Cursor = Cursors.WaitCursor

        Try

 

            Dim node As TreeNode = Me.SelectedNode

            If node Is Nothing Then Return

 

            node.TreeView.LabelEdit = True

            node.BeginEdit()

 

        Catch ex As Exception

            MsgBox(ex.Message)

        Finally

            Me.Cursor = Cursors.Default

        End Try

 

    End Sub

 

    Private Sub SpostaSu(ByVal sender As Object, ByVal e As EventArgs)

 

        Me.Cursor = Cursors.WaitCursor

        Try

            Dim node As TreeNode = Me.SelectedNode

            Call MoveUp(node)

        Catch ex As Exception

            '

        Finally

            Me.Cursor = Cursors.Default

        End Try

 

    End Sub

 

    Private Sub SpostaGiu(ByVal sender As Object, ByVal e As EventArgs)

 

        Me.Cursor = Cursors.WaitCursor

        Try

            Dim node As TreeNode = Me.SelectedNode

            Call MoveDown(node)

        Catch ex As Exception

            '

        Finally

            Me.Cursor = Cursors.Default

        End Try

 

    End Sub

 

    Private Sub EliminaNodo(ByVal sender As Object, ByVal e As EventArgs)

 

        Me.Cursor = Cursors.WaitCursor

        Try

            Dim node As TreeNode = Me.SelectedNode

            Call DeleteNode(node)

        Catch ex As Exception

            '

        Finally

            Me.Cursor = Cursors.Default

        End Try

 

    End Sub

 

#End Region

 

#Region "Eventi"

 

    Private Sub DataTree_AfterLabelEdit(ByVal sender As Object, ByVal e As System.Windows.Forms.NodeLabelEditEventArgs) Handles Me.AfterLabelEdit

 

        ' Modifica il testo nel database

 

        Dim Dt As DataTable = Nothing

        Dim EditedRow As DataRow = Nothing

 

        EditedRow = CType(e.Node.Tag, DataRow)

        Dt = EditedRow.Table

 

        If Dt.TableName = StrTableName(0) Then

            If e.Label = "" Then

                EditedRow.Item(StrFieldDescription(0)) = e.Node.Text

            Else

                EditedRow.Item(StrFieldDescription(0)) = e.Label

            End If

        ElseIf Dt.TableName = StrTableName(1) Then

            If e.Label = "" Then

                EditedRow.Item(StrFieldDescription(1)) = e.Node.Text

            Else

                EditedRow.Item(StrFieldDescription(1)) = e.Label

            End If

        ElseIf Dt.TableName = StrTableName(2) Then

            If e.Label = "" Then

                EditedRow.Item(StrFieldDescription(2)) = e.Node.Text

            Else

                EditedRow.Item(StrFieldDescription(2)) = e.Label

            End If

        End If

 

    End Sub

 

    Private Sub DataTree_KeyUp(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles Me.KeyUp

 

        If e.KeyCode = Keys.Delete Then

            EliminaNodo(sender, e)

        End If

 

    End Sub

 

    Private Sub DataTree_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Me.MouseUp

 

        Select Case e.Button

            Case Windows.Forms.MouseButtons.Right

 

                CtxMenu.Show(Me, New Point(e.X, e.Y))

                Return

 

            Case Else

        End Select

    End Sub

 

 

#End Region

 

#Region "Eventi Pubblici"

 

    Public Event BeforeDeleteNode(ByVal sender As Object, ByVal e As TreeViewCancelEventArgs)

 

#End Region

 

End Class

 

Categoria: VB.NET
mercoledì, 05 dic 2007 Ore. 16.23
Ora e Data
Statistiche
  • Views Home Page: 29.084
  • Views Posts: 37.991
  • Views Gallerie: 3.663
  • n° Posts: 11
  • n° Commenti: 10
Copyright © 2002-2007 - Blogs 2.0
dotNetHell.it | Home Page Blogs
ASP.NET 2.0 Windows 2003