martedì 8 novembre 2011

[VB.NET] Gestione Avanzata Controlli

Descrizione :
Un mio progetto con numerose idee sulla gestione avanzata di controlli Windows Forms.

+ Articolo :

In questo articolo mi sono divertito un po' a mettere insieme una tecnica sulla gestione di controlli a runtime in VB.NET.
Nella fattispecie parliamo di una Classe, la "PBox", che eredita da PictureBox, cercando di sfruttare, o almeno di dare un'indicazione a tutti quelli che vorranno provare, su come sfruttare l'enorme potenzialità di VB.NET, soprattutto se confrontato con il "fratellino" VB6, dal quale moltissimi ancora stentano a separarsi.

Lo scopo dell'articolo, che vuole essere soprattutto propositivo, e che quindi certamente risente di scelte personali e soggettive ( salterà all'occhio in questo caso, quella di aver fatto a meno dei DataBindings, BindingSource, ecc, e di aver gestito diversamente l'aggiornamento del DataTable... ) , è perciò quello di fornire qualche tecnica interessante sulla gestione in generale dei Controls a runtime. Il tutto inserito in un qualcosa a cui ho voluto dare uno scopo finale.
Di carne al fuoco ce n'è parecchia, perciò cercherò di essere il più stringato possibile.

Il Progetto consente tutte le seguenti operazioni a runtime su oggetti della Classe PBox :

--> Aggiungere nuova PBox in 3 modi :
PBox vuota / PBox da file / PBox con Drag & Drop.

--> Eliminare PBox.

--> Clonare PBox da esistente.

--> Spostare e ridimensionare PBox con il Mouse :
Il Container è uno ScrollableControl ( in questo caso un Panel ), e le operazioni di spostamento possono portare a gestire un'area delle dimensioni ben superiori a quella della sola Form visibile.

--> Aggiungere 2 Immagini distinte per ogni PBox, una di sfondo e una principale.

--> Modificare ogni modalità di disegno disponibile per ognuna delle 2 Immagini :
5 Layout diversi per lo sfondo e 5 SizeMode diversi per la principale.

--> Modificare l'ordine di sovrapposizione "ZOrder" con 4 funzioni :
Porta Avanti / Indietro / Primo Piano / Dietro a Tutti.

--> Modificare il colore di sfondo.

--> Salvataggio degli oggetti PBox in formato XML.

--> Caricamento degli oggetti PBox.

--> Salvataggio dell'intero container in formato immagine Bitmap :
Il salvataggio non avviene con un banale capture screen, bensì l'intera area dello ScrollableControl viene trasferita su file Bitmap, comprendendo anche le aree soggette a Scroll e al momento non visibili.

Il tutto viene gestito via Mouse e con una speciale "Form Menu", richiamata con un Click Destro sulla PBox, che non è necessario aprire e chiudere ogni volta per ogni PBox che si vuole modificare, ma che una volta aperta, sta in primo piano sulla Form dell'applicazione e si ancora automaticamente alla PBox selezionata.

Per ragioni di semplicità, e visto anche che le funzioni che volevo implementare erano ben altre, ho volutamente scelto di non gestire controlli inseriti a design. Dal momento che tutta la situazione si può salvare, credo sia un aspetto di importanza minima. Ovviamente la gestione degli errori è quella essenziale. Ho evitato di inserire Try Catch in ogni situazione passibile di errore. In ogni caso l'applicazione funziona perfettamente.

Dato che ho deciso di allegare direttamente i sorgenti, evito la lunga descrizione dei controlli con nomi e proprietà da impostare a design.

Come spesso accade, un'immagine vale più di mille parole :



Il DataGridView in alto è solo a scopo descrittivo e mostra come ogni operazione mappata sugli oggetti PBox viene registrata in una Classe apposita, che eredita da DataTable, e che si occupa di tutte le operazioni esterne, aggiunta, eliminazione, clonazione, salvataggio e caricamento XML.

Al di sotto dei pulsanti c'è il Panel, ossia il contenitore in cui inserire e gestire i Controls PBox.
L'immagine mostra 4 PBox con altrettante immagini PNG trasparenti, due con immagine di sfondo, e due senza. Questo per dare un'idea di come l'oggetto "capisca" che tipo di immagine contiene e la mostri correttamente. Per le GIF è ancora meglio, in quanto, se una .gif inserita come immagine principale, è animata, allora la PBox mostrerà anche l'animazione.

FILE e CLASSI :

1. DTPBoxes.vb :
La classe che eredita da DataTable e si occupa della memorizzazione e della gestione degli Oggetti Pbox.

'***********************************************************************************
'************** Gestione Avanzata Controlli PBox ***** MARCOGG 2011 ****************
'***********************************************************************************

Public Class DTPBoxes
    Inherits DataTable

    Private m_parentcontrol As Control
    Private m_iddisp As Integer
    Private m_cloneoffsetxy As Integer = 20
    Private m_propertyname As String
    Private m_propertyvalue As String

    Public ReadOnly Property IdDisp() As Integer
        Get
            If Me.Rows.Count = 0 Then Return 1
            m_iddisp = 0
            Do
                m_iddisp += 1
                If Me.Rows.Find(m_iddisp) Is Nothing Then Return m_iddisp
            Loop
        End Get
    End Property

    Public Sub New(ByVal parentControl As Control)

        m_parentcontrol = parentControl
        Me.TableName = "PBox"

        'PK
        Me.Columns.Add("ID") '0
        Me.PrimaryKey = New DataColumn() {Me.Columns("ID")}

        'Campi
        With Me.Columns

            .Add("ZOrder", System.Type.GetType("System.Int32")) '1
            '-----------------------------
            .Add("BackColor")
            .Add("FileImmagineSfondo")
            .Add("BackgroundImageLayout")
            .Add("FileImmagine")
            .Add("SizeMode")

            .Add("Left")
            .Add("Top")
            .Add("RealLeft")
            .Add("RealTop")
            .Add("Width")
            .Add("Height")
            .Add("MinimumSize")

        End With

    End Sub

    Public Sub Aggiorna(ByVal pbx As PBox)

        Dim pbxRow As DataRow = Me.Rows.Find(pbx.ID)
        If pbxRow IsNot Nothing Then
            For i As Integer = 1 To Me.Columns.Count - 1
                m_propertyname = Me.Columns(i).ColumnName
                pbxRow.Item(m_propertyname) = pbx.GetPropertyValue(m_propertyname)
            Next
        End If

    End Sub

    Public Sub AggiornaTutti()

        For Each C As Control In m_parentcontrol.Controls
            If TypeOf (C) Is PBox Then Me.Aggiorna(DirectCast(C, PBox))
        Next

    End Sub

    Public Sub Aggiungi(ByVal pbx As PBox)

        With pbx
            .ID = Me.IdDisp
            m_parentcontrol.Controls.Add(pbx)
            .BringToFront()
            Me.Rows.Add(New Object() {.ID})
        End With

        Me.AggiornaTutti()

    End Sub

    Public Sub Clona(ByVal pbx As PBox)

        Dim newPbx As New PBox

        Me.Aggiungi(newPbx)

        newPbx.AggiornaDTPEnabled = False
        For i As Integer = 2 To Me.Columns.Count - 1
            m_propertyname = Me.Columns(i).ColumnName
            m_propertyvalue = pbx.GetPropertyValue(m_propertyname)
            newPbx.SetPropertyValue(m_propertyname, m_propertyvalue)
        Next
        newPbx.Left += m_cloneoffsetxy
        newPbx.Top += m_cloneoffsetxy
        newPbx.AggiornaDTPEnabled = True

    End Sub

    Public Sub Rimuovi(ByVal pbx As PBox)

        pbx.AggiornaDTPEnabled = False
        Me.Rows.Remove(Me.Rows.Find(pbx.ID))
        m_parentcontrol.Controls.Remove(pbx)
        Me.AggiornaTutti()

    End Sub

    Public Sub RimuoviTutti()

        For i As Integer = m_parentcontrol.Controls.Count - 1 To 0 Step -1
            If TypeOf (m_parentcontrol.Controls(i)) Is PBox Then Me.Rimuovi(m_parentcontrol.Controls(i))
        Next

    End Sub

    Public Sub Salva()

        Me.DefaultView.Sort = "ZOrder DESC"
        Me.DefaultView.ToTable.WriteXml(percorso & nomeFileSalvataggio)

    End Sub

    Private Sub CaricaXml(ByVal nomeFileXml As String)

        Me.RimuoviTutti()
        Me.Clear()
        Me.ReadXml(nomeFileXml)

        Dim pbx As PBox

        For row As Integer = 0 To Me.Rows.Count - 1

            pbx = New PBox
            pbx.AggiornaDTPEnabled = False
            m_parentcontrol.Controls.Add(pbx)
            For col As Integer = 0 To Me.Columns.Count - 1
                m_propertyname = Me.Columns(col).ColumnName
                m_propertyvalue = Me.Rows(row).Item(m_propertyname)
                pbx.SetPropertyValue(m_propertyname, m_propertyvalue)
            Next
            pbx.PrimoPiano()
            pbx.Left = pbx.RealLeft
            pbx.Top = pbx.RealTop
            pbx.AggiornaDTPEnabled = True

        Next

        AggiornaTutti()

    End Sub

    Public Sub Carica()
        Try
            CaricaXml(percorso & nomeFileSalvataggio)
        Catch ex As Exception
        End Try
    End Sub

    Public Sub SelezionaSuDgv(ByVal pbx As PBox)

        With DirectCast(m_parentcontrol.Parent, FormMain).dgvtest
            For Each r As DataGridViewRow In .Rows
                If r.Cells("ID").Value = pbx.ID Then r.Selected = True
            Next
        End With

    End Sub

End Class


2. FormMain.vb :
La Form di avvio.


L'apparente complessità della routine sotto il Button cmd_salvasnapshot su FormMain, è dovuta al workaround di un bug che affligge, a quanto pare, il Metodo DrawToBitmap(). Perciò non è imputabile al mio codice. Effettivamente, DrawToBitmap sballa l'ordine degli ZOrders ( Primo Piano, piani intermedi, Ultimo Piano... ). L'ordine dei piani delle PBox nel PNL viene invertito, nell'immagine bitmap risultante. Il problema perciò viene qui risolto con un workaround che inverte temporaneamente gli ZOrder delle PBox, per poi riportarli ai valori originari.

Public Class FormMain

    Private Sub FormMain_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        With PNL
            .AllowDrop = True
            .AutoScroll = True
            .Controls.Clear()
        End With
        DTP = New DTPBoxes(PNL)
        With dgvtest
            .MultiSelect = False
            .SelectionMode = DataGridViewSelectionMode.FullRowSelect
            .DataSource = DTP
        End With
    End Sub

    Private Sub PNL_DragDrop(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles PNL.DragDrop
        Dim fn As String = CType(e.Data.GetData(DataFormats.FileDrop), Array).GetValue(0).ToString
        Try
            Dim newPbx As New PBox
            newPbx.FileImmagine = fn
            DTP.Aggiungi(newPbx)
        Catch ex As Exception
            MessageBox.Show("File non supportato.", "Operazione Annullata", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
        End Try
    End Sub

    Private Sub PNL_DragEnter(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles PNL.DragEnter
        If (e.Data.GetDataPresent(DataFormats.FileDrop)) Then e.Effect = DragDropEffects.Copy
    End Sub

    Private Sub cmd_aggiungivuota_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmd_aggiungivuota.Click
        DTP.Aggiungi(New PBox)
    End Sub

    Private Sub cmd_carica_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmd_carica.Click
        DTP.Carica()
    End Sub

    Private Sub cmd_salva_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmd_salva.Click
        DTP.Salva()
    End Sub

    Private Sub cmd_aggiungifile_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmd_aggiungifile.Click
        Dim fn As String = GetFileImmagine()
        If Not fn = String.Empty Then
            Dim newPbx As New PBox
            newPbx.FileImmagine = fn
            DTP.Aggiungi(newPbx)
        End If
    End Sub

    Private Sub cmd_salvasnapshot_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmd_salvasnapshot.Click

        '-----------------------------------------
        'Inversione degli ZOrder per Bug ZOrder :
        Dim ids As New List(Of Integer)
        Dim zorders As New List(Of Integer)
        For Each pbx As PBox In PNL.Controls
            ids.Add(pbx.ID)
            zorders.Add(pbx.ZOrder)
        Next
        zorders.Reverse()
        For i As Integer = 0 To ids.Count - 1
            For Each pbx As PBox In PNL.Controls
                If pbx.ID = ids(i) Then pbx.ZOrder = zorders(i)
            Next
        Next
        '-----------------------------------------

        PNL.AutoScrollPosition = New Point(0, 0)
        My.Application.DoEvents()

        Dim pnlW As Integer = PNL.Width
        Dim pnlH As Integer = PNL.Height
        Dim W As Integer
        Dim H As Integer
        For Each C As Control In PNL.Controls
            If C.Right > W Then W = C.Right
            If C.Bottom > H Then H = C.Bottom
        Next

        Try
            W += SystemInformation.VerticalScrollBarWidth
            H += SystemInformation.HorizontalScrollBarHeight
            PNL.Width = W
            PNL.Height = H
            Using bmp As New Bitmap(W, H)
                PNL.DrawToBitmap(bmp, New Rectangle(Point.Empty, bmp.Size))
                bmp.Save(percorso & nomeFileSnapShot, Imaging.ImageFormat.Bmp)
            End Using
        Catch ex As Exception
            MessageBox.Show("Impossibile creare l'immagine.", "Errore", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
        Finally
            PNL.Width = pnlW
            PNL.Height = pnlH
        End Try

        '-----------------------------------------
        'Ripristino Zorders per Bug Zorder
        zorders.Reverse()
        For i As Integer = 0 To ids.Count - 1
            For Each pbx As PBox In PNL.Controls
                If pbx.ID = ids(i) Then pbx.ZOrder = zorders(i)
            Next
        Next
        '-----------------------------------------

        MessageBox.Show("File SnapShot Bitmap creato.", "OK", MessageBoxButtons.OK, MessageBoxIcon.Information)

    End Sub

End Class

3. FormMenu.vb :
Form per il Menu "Mobile".


Public Class FormMenu

    Private m_pbx As PBox

    Public Property PBX() As PBox
        Get
            Return m_pbx
        End Get
        Set(ByVal value As PBox)
            m_pbx = value
            Me.Text = m_pbx.ID

            'Valore Combo per BackgroundImageLayout di m_pbx
            cmb_backgroundimagelayout.SelectedItem = [Enum].Format(GetType(ImageLayout), m_pbx.BackgroundImageLayout, "F")
            'Valore Combo per SizeMode di m_pbx
            cmb_sizemode.SelectedItem = [Enum].Format(GetType(PictureBoxSizeMode), m_pbx.SizeMode, "F")
            'Label BackColor
            lbl_backcolor.BackColor = m_pbx.BackColor

            Riposiziona()
        End Set
    End Property

    Private Sub FormMenu_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        'Proprietà
        Me.ShowInTaskbar = False
        lbl_backcolor.Cursor = Cursors.Hand
        'Combo per BackgroundImageLayout
        With cmb_backgroundimagelayout
            .Items.AddRange([Enum].GetNames(GetType(ImageLayout)))
            .DropDownStyle = ComboBoxStyle.DropDownList
        End With
        'Combo per SizeMode
        With cmb_sizemode
            .Items.AddRange([Enum].GetNames(GetType(PictureBoxSizeMode)))
            .DropDownStyle = ComboBoxStyle.DropDownList
        End With
    End Sub

    Private Sub Riposiziona()

        Me.Visible = False
        Dim scrw As Integer = Screen.PrimaryScreen.Bounds.Width
        Dim scrh As Integer = Screen.PrimaryScreen.Bounds.Height
        Me.Left = m_pbx.PointToScreen(New Point(0, 0)).X + m_pbx.Width
        Me.Top = m_pbx.PointToScreen(New Point(0, 0)).Y
        If Me.Right > scrw Then Me.Left = m_pbx.PointToScreen(New Point(0, 0)).X - Me.Width
        If Me.Bottom > scrh Then Me.Top = m_pbx.PointToScreen(New Point(0, 0)).Y - Me.Height
        If Me.Left < 0 Then Me.Left = m_pbx.PointToScreen(New Point(0, 0)).X + m_pbx.Width
        If Me.Top < 0 Then Me.Top = 0
        Me.Visible = True

    End Sub

    Private Sub FormMenu_MouseClick(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Me.MouseClick
        If e.Button = Windows.Forms.MouseButtons.Right Then Me.Close()
    End Sub

    Private Sub cmd_elimina_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmd_elimina.Click
        DTP.Rimuovi(m_pbx)
        Me.Close()
    End Sub

    Private Sub cmd_clona_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmd_clona.Click
        DTP.Clona(m_pbx)
    End Sub

    Private Sub lbl_backcolor_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lbl_backcolor.Click
        Dim CD As New ColorDialog
        If CD.ShowDialog() = DialogResult.OK Then
            lbl_backcolor.BackColor = CD.Color
            m_pbx.BackColor = CD.Color
        End If
    End Sub

    Private Sub cmd_immaginesfondo_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmd_immaginesfondo.Click
        Dim fn As String = GetFileImmagine()
        If Not fn = String.Empty Then m_pbx.FileImmagineSfondo = fn
    End Sub

    Private Sub cmd_immagine_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmd_immagine.Click
        Dim fn As String = GetFileImmagine()
        If Not fn = String.Empty Then m_pbx.FileImmagine = fn
    End Sub

    Private Sub cmb_backgroundimagelayout_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmb_backgroundimagelayout.SelectedIndexChanged
        Dim strSel As String = DirectCast(cmb_backgroundimagelayout.SelectedItem, String)
        m_pbx.BackgroundImageLayout = DirectCast([Enum].Parse(GetType(ImageLayout), strSel), ImageLayout)
    End Sub

    Private Sub cmb_sizemode_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmb_sizemode.SelectedIndexChanged
        Dim strSel As String = DirectCast(cmb_sizemode.SelectedItem, String)
        m_pbx.SizeMode = DirectCast([Enum].Parse(GetType(PictureBoxSizeMode), strSel), PictureBoxSizeMode)
    End Sub

    Private Sub cmd_primopiano_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmd_primopiano.Click
        m_pbx.PrimoPiano()
    End Sub

    Private Sub cmd_portaavanti_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmd_portaavanti.Click
        m_pbx.PortaAvanti()
    End Sub

    Private Sub cmd_dietrotutti_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmd_dietrotutti.Click
        m_pbx.DietroTutti()
    End Sub

    Private Sub cmd_portaindietro_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmd_portaindietro.Click
        m_pbx.PortaIndietro()
    End Sub

End Class


4. ModuloPublics.vb :

Module ModuloPublics

    Public DTP As DTPBoxes
    Public percorso As String = Application.StartupPath & "\"
    Public nomeFileSalvataggio As String = "SalvataggioPBoxes.xml"
    Public nomeFileSnapShot As String = "SnapShot_PBoxes.bmp"

    Public Function GetFileImmagine() As String

        Dim fn As String = String.Empty
        Using OFD As New OpenFileDialog
            OFD.Title = "Selezionare File Immagine"
            OFD.Filter = "Immagini Supportate (*.bmp,*.jpg,*.jpeg,*.gif,*.png)" & _
                                             "|*.bmp;*.jpg;*.jpeg;*.gif;*.png"
            OFD.FilterIndex = 1
            OFD.InitialDirectory = My.Computer.FileSystem.SpecialDirectories.MyPictures
            If OFD.ShowDialog = Windows.Forms.DialogResult.OK Then fn = OFD.FileName
        End Using
        Return fn

    End Function

    Public Function GetImmagine(ByVal nomeFile As String) As Image

        Dim MS As New IO.MemoryStream(IO.File.ReadAllBytes(nomeFile))
        Return Image.FromStream(MS)

    End Function

End Module

5. PBox.vb :
In particolare vorrei far notare i due Metodi Reflection GetPropertyValue e SetPropertyValue, con cui è possibile leggere e impostare proprietà attraverso le relative definizioni in formato Stringa. Questo rende ancora più facile l'eventuale aggiunta di altre proprietà da mappare e gestire, e la conseguente serializzazione su file XML.

'***********************************************************************************
'************** Gestione Avanzata Controlli PBox ***** MARCOGG 2011 ****************
'***********************************************************************************

Public Class PBox
    Inherits PictureBox

    Private m_id As Integer 'ID univoco in DTI per salvataggio / caricamento
    Private m_realleft As Integer 'Left effettivo in caso di Scroll orizzontale
    Private m_realtop As Integer 'Top effettivo in caso di Scroll verticale

    Private m_aggiornadtpenabled As Boolean = True 'Switch abilita/disabilita aggiornamento row DTI

    Private m_tiposelezione As TipiSelezione = TipiSelezione.NS
    Private m_resizewidthheight As Integer = 10 'Altezza/Larghezza Rect Resize
    Private m_rectmovexy As Rectangle 'Rect per Move XY
    Private m_rectresizewe As Rectangle 'Rect per Resize WE
    Private m_rectresizens As Rectangle 'Rect per Resize NS
    Private m_rectresizenwse As Rectangle 'Rect per Resize NWSE
    Private m_openmenu As Boolean 'Check per Menu Opzioni
    Private m_selx As Integer
    Private m_sely As Integer

    Private m_fileimmaginesfondo As String = String.Empty
    Private m_fileimmagine As String = String.Empty

    Public Property ID() As Integer
        Get
            Return m_id
        End Get
        Set(ByVal value As Integer)
            m_id = value
        End Set
    End Property

    Public Property ZOrder() As Integer
        Get
            If Me.Parent IsNot Nothing Then
                Return Me.Parent.Controls.GetChildIndex(Me)
            Else
                Return 0
            End If
        End Get
        Set(ByVal value As Integer)
            If Me.Parent IsNot Nothing Then
                Me.Parent.Controls.SetChildIndex(Me, value)
                Me.AggiornaTuttiDTP()
            End If
        End Set
    End Property

    Public Property RealLeft() As Integer
        Get
            Return m_realleft
        End Get
        Set(ByVal value As Integer)
            m_realleft = value
        End Set
    End Property

    Public Property RealTop() As Integer
        Get
            Return m_realtop
        End Get
        Set(ByVal value As Integer)
            m_realtop = value
        End Set
    End Property

    Public Property AggiornaDTPEnabled() As Boolean
        Get
            Return m_aggiornadtpenabled
        End Get
        Set(ByVal value As Boolean)
            m_aggiornadtpenabled = value
        End Set
    End Property

    Public Property TipoSelezione() As TipiSelezione
        Get
            Return m_tiposelezione
        End Get
        Set(ByVal value As TipiSelezione)
            m_tiposelezione = value
            Select Case m_tiposelezione
                Case TipiSelezione.MoveXY
                    Cursor = Cursors.SizeAll
                Case TipiSelezione.ResizeWE
                    Cursor = Cursors.SizeWE
                Case TipiSelezione.ResizeNS
                    Cursor = Cursors.SizeNS
                Case TipiSelezione.ResizeNWSE
                    Cursor = Cursors.SizeNWSE
                Case Else
                    Cursor = Cursors.Default
            End Select
        End Set
    End Property

    Public Property FileImmagineSfondo() As String
        Get
            Return m_fileimmaginesfondo
        End Get
        Set(ByVal value As String)
            m_fileimmaginesfondo = value
            If Not m_fileimmaginesfondo = String.Empty Then
                Me.BackgroundImage = GetImmagine(m_fileimmaginesfondo)
                AggiornaMeDTP()
            End If
        End Set
    End Property

    Public Property FileImmagine() As String
        Get
            Return m_fileimmagine
        End Get
        Set(ByVal value As String)
            m_fileimmagine = value
            If Not m_fileimmagine = String.Empty Then
                Dim sizeTemp As Size = Me.Size
                Me.Image = GetImmagine(m_fileimmagine)
                Me.Size = sizeTemp
                AggiornaMeDTP()
            End If
        End Set
    End Property

#Region "Costruttore"

    Public Sub New()

        Me.BackColor = Color.White
        Me.MinimumSize = New Size(m_resizewidthheight * 2, m_resizewidthheight * 2)
        Me.BackgroundImageLayout = ImageLayout.None
        m_rectmovexy = New Rectangle(0, 0, Me.Width - m_resizewidthheight, Me.Height - m_resizewidthheight)
        m_rectresizewe = New Rectangle(Me.Width - m_resizewidthheight, 0, m_resizewidthheight, Me.Height - m_resizewidthheight)
        m_rectresizens = New Rectangle(0, Me.Height - m_resizewidthheight, Me.Width - m_resizewidthheight, m_resizewidthheight)
        m_rectresizenwse = New Rectangle(Me.Width - m_resizewidthheight, Me.Height - m_resizewidthheight, m_resizewidthheight, m_resizewidthheight)

    End Sub

#End Region

#Region "Metodi"

    'Metodo Reflection GetPropertyValue : lettura valore proprietà
    Public Function GetPropertyValue(ByVal propertyName As String) As String

        Dim propertyValue As Object
        Dim PI As System.Reflection.PropertyInfo
        PI = Me.GetType.GetProperty(propertyName)
        propertyValue = PI.GetValue(Me, Nothing)

        With PI.PropertyType
            If .IsEnum Then propertyValue = Convert.ToInt32(propertyValue)
            If .Name = "Color" Then propertyValue = ColorTranslator.ToWin32(Me.BackColor)
            If .Name = "Size" Then propertyValue = CType(propertyValue, Size).Width & "|" & CType(propertyValue, Size).Height
        End With
        If propertyValue IsNot Nothing Then
            Return propertyValue.ToString
        Else
            Return String.Empty
        End If

    End Function

    'Metodo Reflection SetPropertyValue : assegnazione valore proprietà
    Public Sub SetPropertyValue(ByVal propertyName As String, ByVal propertyValue As Object)

        Dim PI As System.Reflection.PropertyInfo
        PI = Me.GetType.GetProperty(propertyName)
        If Not PI.CanWrite Then Exit Sub

        With PI.PropertyType
            If .IsEnum Then propertyValue = Convert.ToInt32(propertyValue)
            If .Name = "Int32" Then propertyValue = Convert.ToInt32(propertyValue)
            If .Name = "Color" Then propertyValue = ColorTranslator.FromWin32(propertyValue)
            If .Name = "Size" Then propertyValue = New Size(propertyValue.ToString.Split("|")(0), _
                                   propertyValue.ToString.Split("|")(1))
        End With
        PI.SetValue(Me, propertyValue, Nothing)

    End Sub

    Private Sub AggiornaMeDTP()
        If DTP IsNot Nothing And Me.AggiornaDTPEnabled = True Then DTP.Aggiorna(Me)
    End Sub

    Private Sub AggiornaTuttiDTP()
        If DTP IsNot Nothing And Me.AggiornaDTPEnabled = True Then DTP.AggiornaTutti()
    End Sub

    Public Sub PortaAvanti()
        Me.ZOrder -= 1
    End Sub

    Public Sub PortaIndietro()
        Me.ZOrder += 1
    End Sub

    Public Sub PrimoPiano()
        Me.BringToFront()
        Me.AggiornaTuttiDTP()
    End Sub

    Public Sub DietroTutti()
        Me.SendToBack()
        Me.AggiornaTuttiDTP()
    End Sub

#End Region

#Region "Eventi"

    Protected Overrides Sub OnResize(ByVal e As System.EventArgs)

        'Aggiorna posizione e dimensione dei Rect di selezione
        m_rectmovexy.Width = Me.Width - m_resizewidthheight
        m_rectmovexy.Height = Me.Height - m_resizewidthheight

        m_rectresizewe.X = Me.Width - m_resizewidthheight
        m_rectresizewe.Height = Me.Height - m_resizewidthheight

        m_rectresizens.Y = Me.Height - m_resizewidthheight
        m_rectresizens.Width = Me.Width - m_resizewidthheight

        m_rectresizenwse.X = Me.Width - m_resizewidthheight
        m_rectresizenwse.Y = Me.Height - m_resizewidthheight

        Me.AggiornaMeDTP()
        Me.Refresh()

    End Sub

    Protected Overrides Sub OnLocationChanged(ByVal e As System.EventArgs)

        Me.RealLeft = Me.Left - DirectCast(Me.Parent, ScrollableControl).AutoScrollPosition.X
        Me.RealTop = Me.Top - DirectCast(Me.Parent, ScrollableControl).AutoScrollPosition.Y
        Me.AggiornaMeDTP()

    End Sub

    Protected Overrides Sub OnBackColorChanged(ByVal e As System.EventArgs)

        MyBase.OnBackColorChanged(e)
        Me.AggiornaMeDTP()

    End Sub

    Protected Overrides Sub OnMouseClick(ByVal e As System.Windows.Forms.MouseEventArgs)

        m_openmenu = False
        For Each F As Form In My.Application.OpenForms
            If TypeOf (F) Is FormMenu Then
                m_openmenu = True
                DirectCast(F, FormMenu).PBX = Me
                Exit For
            End If
        Next

        If e.Button = Windows.Forms.MouseButtons.Left Then
            If DTP IsNot Nothing Then DTP.SelezionaSuDgv(Me)
        End If

        If e.Button = Windows.Forms.MouseButtons.Right Then
            If m_openmenu = False Then
                Dim FM As New FormMenu
                FM.Show()
                FM.PBX = Me
            End If
        End If

    End Sub

    Protected Overrides Sub OnMouseDown(ByVal e As System.Windows.Forms.MouseEventArgs)

        m_selx = e.X
        m_sely = e.Y
        If m_rectmovexy.Contains(m_selx, m_sely) Then Me.TipoSelezione = TipiSelezione.MoveXY
        If m_rectresizewe.Contains(m_selx, m_sely) Then Me.TipoSelezione = TipiSelezione.ResizeWE
        If m_rectresizens.Contains(m_selx, m_sely) Then Me.TipoSelezione = TipiSelezione.ResizeNS
        If m_rectresizenwse.Contains(m_selx, m_sely) Then Me.TipoSelezione = TipiSelezione.ResizeNWSE

    End Sub

    Protected Overrides Sub OnMouseUp(ByVal e As System.Windows.Forms.MouseEventArgs)

        Me.TipoSelezione = TipiSelezione.NS

    End Sub

    Protected Overrides Sub OnMouseMove(ByVal e As System.Windows.Forms.MouseEventArgs)

        'MouseMove senza selezione
        If Me.TipoSelezione = TipiSelezione.NS Then
            If m_rectmovexy.Contains(e.X, e.Y) Then Cursor = Cursors.SizeAll
            If m_rectresizewe.Contains(e.X, e.Y) Then Cursor = Cursors.SizeWE
            If m_rectresizens.Contains(e.X, e.Y) Then Cursor = Cursors.SizeNS
            If m_rectresizenwse.Contains(e.X, e.Y) Then Cursor = Cursors.SizeNWSE
        End If

        'MouseMove con selezione
        If Me.TipoSelezione = TipiSelezione.MoveXY Then
            Me.Left -= (m_selx - e.X)
            Me.Top -= (m_sely - e.Y)
        End If
        If Me.TipoSelezione = TipiSelezione.ResizeWE Then
            Me.Width += e.X - m_selx
            m_selx = e.X
        End If
        If Me.TipoSelezione = TipiSelezione.ResizeNS Then
            Me.Height += e.Y - m_sely
            m_sely = e.Y
        End If
        If Me.TipoSelezione = TipiSelezione.ResizeNWSE Then
            Me.Width += e.X - m_selx
            Me.Height += e.Y - m_sely
            m_selx = e.X
            m_sely = e.Y
        End If

    End Sub

#End Region

End Class

6. TipiSelezione.vb :
Public Enum TipiSelezione

    NS = 0
    MoveXY = 1
    ResizeWE = 2
    ResizeNS = 3
    ResizeNWSE = 4

End Enum

Più che un singolo How-To, lo considero un insieme di tanti spunti, come ad esempio :

--> dotare una Classe di Metodi di accesso generico con la Reflection
--> popolare una ComboBox ( quelle su Form Menu ) direttamente con una Enum e farsi restituire il valore corrispondente
--> salvare l'intero contenuto di un Panel in una bitmap, comprese le aree non visibili
--> una parziale serializzazione dei Controls Forms, ecc, ecc...

+ Fine Articolo.

Un Click su "Mi Piace" è il modo migliore per ringraziare l'autore di questo articolo.



1 commenti:

Anonimo ha detto...

Inѕpirіng queѕt thеre.
What occurred after? Thanks!

Here is my wеbsite - Chios studios

Posta un commento

 
Design by Free WordPress Themes Modificato da MarcoGG