di Leonardo Alario
Già
da tempo, nel team di sviluppo dell’azienda per cui lavoro, stava
“balenando” l’idea di costruire un controllo che gestisse, attraverso
schemi xml, i controlli opportunamente mappati presenti sui form dei
nostri applicativi.
Durante lo sviluppo di un progetto, i
giorni uomo impiegati per progettare e sviluppare la GUI incide in modo
notevole sulla durata del progetto stesso, sia per gestire la
presentation, che per gestire l’aspetto funzionale di come la GUI deve
interagire con il cliente stesso.
Ed ecco l’idea: mettere in
piedi un meccanismo tale che tutti i controlli, sui nostri form, siano
manipolabili dall’esterno, permettendoci magari di personalizzare
l’applicativo dal cliente solo con qualche click, sia dal punto di
vista grafico, che funzionale.
Così facendo, risparmieremmo
preziosi giorni uomo sui nostri progetti, occupandoci di più della
logica funzionale, rispetto alla presentazione dell’applicativo stesso.
Armato di Reflection e dell’interfaccia IExtenderProvider, mi sono messo a lavoro.
Come funziona il controllo
Avevo già pensato a come il meccanismo di persistenza del controllo dovesse funzionare:
devo utilizzare un file xml per le skin contenente la mappatura di tutti i controlli presenti sul form e coadiuvato da un XmlSkinHelper che si occupa di caricare gli schemi in una lista di tipi generici.
Lo
schema deve prendere in considerazione tutte le famiglie di controlli
disponibili e, a parita di famiglia di controllo, consentire l’utilizzo
di diverse impostazioni.
L’esempio seguente di skin file
contiene la mappatura di diverse famiglie di controlli. Evidenzio che
la famiglia ha due tipologie di mappatura:
- La mappatura generica: riferita alla famiglia generica del controllo (es: <System.Windows.Forms.Button>)
- La mappatura denominata: riferita ad un controllo particolare (es: <System.Windows.Forms.Button value="Prova">>
Ottenuto
lo schema contenente la mappatura dei controlli, viene la parte
difficile: come fare a processare le varie proprietà del controllo ?
Grazie alla reflection ed alla classe PropertyInfo Class (System.Reflection)
il controllo incapsula tutta la logica per il caricamento delle
proprietà dallo schema e per il relativo aggiornamento dei controlli
presenti sul form selezionato.
Il controllo ai raggi X
Il controllo espone diverse proprietà e metodi, come elencato di seguito:
- la proprietà string SkinFile: permette di leggere o settare il nome ed il percorso dello Skin File da applicare o applicato;
- la proprietà bool AtLeastOneControlWasProcessed: indica se almeno una proprietà mappata nello schema, è stata processata con successo;
- la proprietà string HowManyControlWasProcessed: indica il totale delle proprietà processate rispetto a quelle mappate nello schema;
- Il metodo LoadSkinFile(): permette di ricaricare uno skin file già caricato in precedenza da disco;
- Il metodo LoadSkinFile(string SkinFile): permette di caricare il file di skin da disco;
- Il metodo ApplySkin(): permette di applicare ai controlli del form le proprietà specificate nello schema caricato;
- Il metodo ApplySkin(string SkinFile): permette di applicare ai controlli del form le proprietà specificate nello schema indicato;
Per utilizzare il controllo in un progetto è necessario aggiungere:
- un riferimento alla libreria WindowFormSkins.dll;
- aggiungiamo una direttiva Using: using SoftCantieri;
- aggiungiamo una nuova form che eredita da SoftCantieri.WindowFormSkin;
Invocando la form stessa avremo a disposizione i sopracitati metodi e proprietà.
Segue un piccolo esempio di utilizzo del controllo:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
using SoftCantieri;
namespace DemoWindowsFormsSkins
{
public partial class FrmDemo : SoftCantieri.WindowFormSkin
{
private List<DummyBol> _ListaDummy = new List<DummyBol>();
public FrmDemo()
{
InitializeComponent();
}
private void Button1_Click(object sender, CancelEventArgs e)
{
try
{
// Carica il file xml skin selezionato
// e Memorizza lo skin in una collezione di oggetti
openFileDialog.Show();
this.SkinFile = openFileDialog.FileName;
this.LoadSkinFile(this.SkinFile);
// applica lo skin
this.ApplySkin();
TxtSkin.Text = this.SkinFile;
}
catch (Exception Ex)
{
MessageBox.Show("[" + Ex.Source + "]" + "\r" + Ex.Message, Application.ProductName,
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
}
Il progetto allegato
Allegato all’articolo troverete un piccolo progetto demo che
illustra il funzionamento del controllo. Per realizzarlo ho utilizzato
i componenti Ascend.NET
sia per testare lo spettro di controlli supportati sia per deliziare il
palato sopraffino degli utenti sempre più affamati di interfacce
accattivanti.
Se apriamo la soluzione o il progetto della demo allegato e
selezioniamo in design mode un controllo presente sul form, otterremo
la seguente situazione:
per ogni componente sul form verranno aggiunte, grazie
all’interfaccia IextenderProvider e al provider di estensione
implementato internamente nel controllo, le seguenti proprietà:
- bool ControlStyleCanProcess: se impostata a true permette di
processare il controllo selezionato, altrimenti in fase di elaborazione
il controllo verrà ignorato;
- String ControlStyle: a parita di famiglia di
controllo permette di definirne il nome dello stile da applicare a
patto che sia mappato nel file xml di skin con lo stesso nome;
- Bool ControlStyleCanProcessOnEnterEvent: se impostato
a true permette di processare il controllo quando riceve gli eventi del
focus in entrata ed in uscita altrimenti, quando generati gli eventi
del controllo verrano ignorati;
- Color ControlOnEnterForeColor: permette di variare la proprietà forecolor del controllo, con il colore indicato, quando riceve il focus.
- Color ControlOnEnterBackColor: permette di variare la proprietà Backcolor del controllo , con il colore indicato, quando riceve il focus.
- Font ControlOnEnterFont: permette di variare il font del controllo , con il font indicato, quando riceve il focus.
Quando il controllo perde il focus ritornerà nello stato originario.
Ora apriamo il progetto della demo allegata, avviamolo, e selezioniamo lo skin file da applicare.
Nb. ho volutamente inserito uno skin con alcune proprietà
mappate nel controllo in modo errato, quando lo aprirete riceverete
un’eccezione con il nome della proprietà non valida:
quando selezioneremo lo skin file, i controlli nel form verranno
processati e visualizzati con i nuovi valori indicati nello skin file.
Tutti gli skin file xml, non formattati secondo le specifiche
suddette, non verrano presi in considerazione dal controllo; in
sostanza il controllo non cambierà aspetto, così come in presenza di
nomi o di valori delle proprietà errati, in un file di skin aderente
alle specifiche,verranno segnalate all’utente con delle eccezioni.
Conclusioni
Per eventuali segnalazioni di anomalie o chiarimenti sul controllo in questione potete scrivere a questo indirizzo leo.alario.software@email.it .
Sono previste nuove implementazioni ed estensioni per il controllo:
- la possibilità di aggiungere il controllo al form in modo visuale; senza, che il form stesso erediti dal controllo;
- la possibilità di generare gli schemi xml selezionando il form
sorgente, in modo da automatizzare la creazione degli schemi contenenti
i controlli posizionati sul form selezionato;
- il monitoraggio dello skin file selezionato, per verificare se sono intervenute variazioni ed eventualmente ricaricarlo;
- un meccanismo di RollBack allo stato precedente dei controlli
già processati, quando si presenta un eccezione durante l’elaborazione;
Chiunque vorrà, potrà implementare le funzionalità sopraindicate e segnalarmi le eventuali modifiche al codice in allegato.
Spero che questo piccolo componente possa ritornare utile a
qualcuno facendogli risparmiare preziosi giorni uomo, spada di Damocle
sulle teste di noi “poveri” tartassati programmatori.
Note
Come detto in precedenza, nel progetto sono stati utilizzati gli
Ascend.Net controls.Il progetto contiene nella cartella \..\Bin gli
assembly per far funzionare correttamente la demo.
Chiunque fosse interessato può scaricare i controlli o avere maggiori informazioni sugli stessi al seguente link: http://www.codeplex.com/Tagging/TagDetail.aspx?TagName=Ascent.Net