Guarda! Senza mani! - Prima parte
a cura di Sabrina Cosolo e Diego Cattaruzza (requisiti: conoscenza intermedia di Sql Server e di .Net)Premessa
L'idea iniziale era quella di traslare in .Net (e di conseguenza su Sql Server 2005 e ADO.Net 2.0), la serie di articoli già scritta a suo tempo. Nello scrivere il codice per l'esempio, però, sono sorte sempre nuove esigenze, per affrontare le quali non si voleva scrivere codice 'sufficiente al funzionamento', ma codice utile e riciclabile nel futuro.
Sono state così sviluppate una notevole serie di classi, tanto che la serie di articoli su SqlServer-Net è diventata una specie di tutorial su come si scrive una applicazione .Net, come si sviluppa codice riusabile e come lo si usa all'interno dei propri programmi. 'Senza mani' significa 'senza l'aiuto dei wizard e degli automatismi di Visual Basic, per far comprendere meglio come funziona .Net.Quindi si è reso necessario predisporre quella che potrebbe essere la struttura di un progetto articolato (soluzione, progetto libreria base, libreria user interface, libreria dati Applicazione) ed esporre man mano il suo sviluppo, fino a costruire insieme una applicazione che usa il tutto per costruire un database, l'interfaccia per la sua manutenzione e poi l'interfaccia di gestione.
Questa serie di articoli è dunque un 'work in progress', ragion per cui è possibile che alcune delle classi sviluppate ed esposte in un articolo subiscano modifiche in seguito e che il file .zip dell'esempio debba essere ricaricato. Nei limiti del possibile, si cercherà di annotare e spiegare anche le modifiche.
Perché lo stiamo scrivendo a quattro mani
Sabrina pensa in C# ed è 'evoluta', Diego pensa in VB ed è abbastanza 'principiante' da voler spiegare qualche passo a prima vista troppo complesso.Introduzione
La prima serie di articoli su Sql Server verteva su come utilizzare MSDE (o SQL Server 2000) da codice VB6, cercando di spiegare alcune delle basi del funzionamento di un Database Server e come lavorarvi senza "scontri frontali". Oggi siamo passati a Visual Studio .NET 2005 e a SQL Server 2005, nelle versioni Express oppure in quelle Retail.Dal punto di vista del programmatore, non vi sono differenze nell'uso di una versione o l'altra di SQL Server nelle normali attività sui dati. Problemi di tipo complesso - come sedi distribuite sul territorio che si scambiano dati, trasferimenti di dati fra diverse entità (aziende diverse parte di un gruppo) e altre gestioni complesse che esulano dal lavoro quotidiano del programmatore che lavora con i database - possono comportare differenze di approccio con i nuovi strumenti.
Quando si tratta di creare un database, creare una tabella, inserire, modificare, visualizzare i dati di una o più tabelle, SQL Server Express e SQL Server Enterprise funzionano nello stesso modo.
C'è una peculiarità che è stata introdotta in SQL Server Express Edition: quella di poter funzionare "On Demand". Cioè il Database Server viene fatto partire quando il primo utente si connette e viene "spento" quando l'ultimo utente si disconnette. Per una applicazione di tipo personale, ovvero generata per l'uso da parte di una singola persona, può essere un modo corretto di gestire le risorse del sistema, ma per una qualsiasi applicazione di tipo aziendale, usata da uno o più impiegati all'interno di una azienda, non crediamo dia alcun vantaggio.
In questi articoli, pertanto, il Database Server sarà sempre tale (server), e i Database saranno sempre connessi ad esso.Visto che faremo Programmazione Orientata agli Oggetti (OOP), assumiamo che chi legge questo articolo abbia lavorato con VB.NET o C# abbastanza a lungo da sapere cos'è una classe, che cos'è una struttura, che cos'è un'interfaccia, cos'è un evento, cosa significa serializzazione e conosca la sintassi base del linguaggio che intende usare, inoltre, visto che lavoreremo con Visual Studio 2005 assumiamo che sappia cos'è una soluzione, cos'è un progetto, cos'è una form e quali sono i principali tipi di controlli che si possono utilizzare su una form. Ma talvolta ci si diffonderà in qualche spiegazione per principianti.
Assumiamo inoltre che abbia sotto mano o conosca per esperienza il linguaggio SQL standard.
In questa nuova serie di articoli utilizzeremo T-SQL che è il "Dialetto" di SQL Server e probabilmente vi saranno alcune funzioni specifiche (quali le Stored Procedure di sistema e le strutture di sicurezza) che sono peculiari di questo RDBMS, mentre saranno completamente diverse in altri motori database.La soluzione ed i progetti di test sono stati generati utilizzando Visual Studio 2005 Professional Edition in Inglese, pertanto tutti i riferimenti diretti a Visual Studio sono fatti sull'ambiente in questa versione, siamo certi che non avrete comunque difficoltà a trovare le corrispondenze sulla versione italiana o sulle altre versioni disponibili.
Intenzioni
Si intende dare una serie di nozioni di base sul lavoro con i dati tramite ADO.NET e Visual Studio.
Per creare applicazioni che manipolano dati, non basta avere nozioni di base su come funziona ADO, ci sono molte altre cose che ci servono. Partendo dal presupposto che ciascuno di voi studi VB o C# per imparare a costruire applicazioni funzionanti, cercheremo di costruire gli articoli in modo tale da darvi una serie di suggerimenti, ovviamente a titolo personale, su come costruire una applicazione costruendone una per voi.
Pertanto, ecco un manifesto programmatico, di quel che si intende scrivere (staremo a vedere quanto riusciremo a fare :o)). Via via che pubblicheremo gli articoli, essi verranno collegati su questo elenco.
- L'applicazione di test
- Come costruire un gestore personalizzato per i settings di applicazione.
- Come connettersi a un database SQL Server.
- Come generare un database.
- Come generare una tabella.
- Come generare una vista.
- Come generare una stored procedure.
- Come generare una user defined function.
- Come generare un Login di tipo SQL oppure uno di tipo Trusted.
- Come assegnare ad un Login accesso ad un database e il diritto di vedere/modificare dati ed eseguire Stored Procedure.
- Come inserire dati in una tabella.
- Come modificare dati in una tabella.
- Come visualizzare i dati di una tabella.
- Come visualizzare i dati di più tabelle.
- Come generare una interfaccia utente per gestire una tabella.
Abbiamo esagerato?
Non bisogna mai porsi delle mete troppo facili, sennò non c'è gusto.
Giusto per farvi venir l'acquolina in bocca cominciamo.L'applicazione di test
Per lavorare sui vari argomenti genereremo una applicazione di test; assumendo che sappiate utilizzare Visual Studio .NET la soluzione sarà composta come segue:
- Una soluzione vuota che chiameremo UsingSqlServer2005
- Create un progetto di tipo Window Application, chiamandolo UsingSqlserver.
In Solution Explorer scegliete di vedere tutti i file. Se non vedete il nome della Solution, dal menu Tools/Options, in Projects and Solutions/General, impostate Always Show Solution.
Quindi modificate il Name della Solution come indicato.- Un progetto di tipo Windows Forms che chiameremo UsingSqlServer
- Un progetto di tipo Class Library che chiameremo TAndT.Base
- Un progetto di tipo Component Library che chiameremo TAndT.UI
- Un progetto di tipo Component Library che chiameremo TAndT.Data
I quattro progetti saranno rispettivamente la nostra applicazione, una libreria di classi per le strutture e funzionalità di base che potremmo riciclare in seguito per altri usi, una libreria di classi e controlli per l'interfaccia utente, una libreria di classi per la gestione delle funzionalità di accesso ai dati che potremo eventualmente riutilizzare in altre occasioni.
Nel caso ve lo steste chiedendo, TAndT sta per Tips And Tricks.Generati i progetti, configuriamoli come segue:
- Impostiamo il Root namespace per i progetti come segue:
- UsingSqlServer - Namespace: TAndT.UsingSqlServer
- TAndT.Base - Namespace: TAndT.Base
- TAndT.UI - Namespace: TAndT.UI
- TAndT.Data - Namespace TAndT.Data
- Aprendo le proprietà di UsingSqlServer, impostate il Root Namespace come indicato. Per gli altri progetti il cambio non è da effettuare, semplicemente perché le DLL sono denominate come i namespace (vedi best practices).
- Cancelliamo lo Usercontrol1 e la Class1 generate automaticamente nei tre progetti libreria.
- Rinominiamo la Form1 del progetto UsingSqlServer e chiamiamola FrmMain, se per caso il refactoring non facesse tutto in automatico, agiamo a mano e, inoltre (per chi scrive in C#), impostiamo il namespace al valore che abbiamo cambiato nelle Proprietà di progetto.
- Attiviamo il flag IsMdiContainer ponendolo a True in modo che la form base divenga una form MDI.
La struttura di base della nostra applicazione dovrebbe essere pronta, fatto salvo che sui progetti di esempio sono state aggiunte una icona personalizzata da usare per l'applicazione, le librerie e far comparire su tutte le form al posto di quella di sistema. Gli eventuali aggeggi grafici che aggiungeremo rimarranno a disposizione nella cartella di progetto.
Ed ora, per iniziare a costruire la struttura su cui si reggerà il nostro progetto, costruiamo alcune classi che riutilizzeremo in tutta l'applicazione.
Qualche buona abitudine
In tutte le classi dei nostri progetti, sia professionali che di insegnamento, troverete all'interno di ciascuna uno schema piuttosto preciso per la gestione delle eccezioni, questo schema si compone dei seguenti elementi:
- Per i programmatori in Visual Basic, nelle proprietà di ogni progetto, la funzionalità 'application framework' è disabilitata, è impostata Option Strict On e la libreria Microsoft.VisualBasic è esclusa dalle References (VisualBasic c'è sempre, ma così si imparano più cose).
Questo significa che ci si preoccupa di creare una classe, che noi chiamiamo Program, col metodo Main:
Public Class Program <STAThread()> _ Shared Sub Main() Application.EnableVisualStyles() Application.Run(New FrmMain) End Sub End Class- Ogni classe è dotata di una variabile statica che tramite la Reflection recupera il nome della classe, in modo da permetterci di sapere in quale classe si è verificata l'eccezione.
- Ogni metodo all'interno di una classe, tranne alcuni metodi particolari per i quali non avrebbe senso un simile funzionamento, hanno al loro interno una gestione eccezioni che, ove possibile, gestisce le eccezioni localmente, altrimenti rilancia l'eccezione che si è verificata fino alla User Interface. I metodi delle classi della User Interface, agganciano le eccezioni non gestibili ad un messaggio di errore visualizzato che evita il blocco dell'applicazione, cercando per quanto possibile una eventuale uscita "soft". Pertanto abituatevi a trovare queste linee di codice ovunque:
// C# #region Variabili private ///<summary> /// Nome della classe usato per debug nella generazione delle eccezioni ///</summary> private static readonly string mClassName = System.Reflection.MethodBase.GetCurrentMethod().ReflectedType.Name; #endregionTry { } catch (Exception ex) { throw new ApplicationException(" " + mClassName + "." + System.Reflection.MethodBase.GetCurrentMethod().Name, ex); }Try { } catch (Exception ex) { Warnings.Errore(mClassName, System.Reflection.MethodBase.GetCurrentMethod(), ex); }'' VB #Region"Variabili private" ''' <summary> ''' Nome della classe usato per debug nella generazione delle eccezioni ''' </summary> private static readonly string mClassName = System.Reflection.MethodBase.GetCurrentMethod().ReflectedType.Name; #endregionTry Catch ex As Exception Throw New ApplicationException("" + mClassName + "." _ + System.Reflection.MethodBase.GetCurrentMethod.Name, ex) End TryTry Catch ex As Exception Warnings.Errore(mClassName, System.Reflection.MethodBase.GetCurrentMethod(), ex) End TryQuesta è l'occasione giusta per sottolineare come il codice mostrato negli articoli sarà preceduto da commenti in grassetto ad indicare il linguaggio, in base al quale cambia anche il colore di fondo, e mostrerà alcune istruzioni divise in più righe, anche se nel sorgente dell'esempio non vanno a capo, per ragioni di leggibilità.
E' anche l'occasione per suggerire uno Snippet:
<?xml version="1.0" encoding="UTF-8"?> <CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet"> <CodeSnippet Format="1.0.0"> <Header> <Title>Region section</Title> <Author>Diego Cattaruzza</Author> <Description>Inserts a #Region section.</Description> <Shortcut>reg</Shortcut> </Header> <Snippet> <Declarations> <Literal> <ID>RegionTitle</ID> <Type>String</Type> <ToolTip>Replace with a decription for the Region.</ToolTip> <Default>'-'</Default> </Literal> </Declarations> <Code Language="VB" Kind="method body"> <![CDATA[#Region "$RegionTitle$" #End Region]]> </Code> </Snippet> </CodeSnippet> </CodeSnippets>Così, quando volete inserire una Region nel vostro codice, digitate reg e poi pigiate il tasto Tab o Invio (a seconda che scriviate in VBi o in C# (vedi anche articolo sugli Snippet).
Un'altra delle cose che convenzionalmente usiamo è la seguente serie di regole di nomenclatura:
- Le variabili a livello di classe sono in notazione camelCase e iniziano tutte per m (es. mClassName, mNome, mCognome...).
- i parametri degli eventi si chiamano sempre sender ed e
- i parametri di tutti gli altri metodi hanno il nome in notazione camelCase e iniziano con una lettera p (es. pMessage, pName, pValue...) per Sabrina-C#, ma non per Diego-VB.
- le variabili locali a livello di metodo sono in notazione camelCase e fatto salvo per i contatori dei cicli for che usualmente si chiamano i, j, k ecc., hanno un nome significativo es. string fileName, int idArticolo, double valoreTotale.
Precisiamo che questi sono i nostri metodi personali per la scrittura del codice, sicuramente criticabili ed opinabili da altri molto più bravi di noi. Ma a noi piace così, almeno al momento, magari fra un paio d'anni scriveremo in modo del tutto diverso, per ora sopportateci così.
![]()
Veniamo ora alla prima delle classi di supporto e sostegno alle nostre elucubrazioni future.
ExceptionHelper - un aiutino per la gestione delle eccezioni
// C# #region Using directives using System; using System.Collections.Generic; using System.Reflection; using System.Text; #endregion namespace TAndT.Base { public static class ExceptionHelper { #region Variabili private private static readonly string mClassName = System.Reflection.MethodBase.GetCurrentMethod().ReflectedType.Name; #endregion public static string BuildMessage(string pClassName, MethodBase pMethod, Exception ex) { string trattini = new string('-', 80); string separator = new string('-', 10); StringBuilder sb = new StringBuilder(string.Empty); sb.Append(">>"); sb.AppendLine(trattini); sb.AppendFormat("LogTime: {0} {1}", DateTime.Now.ToShortDateString(), DateTime.Now.ToLongTimeString()); sb.AppendLine(); sb.AppendLine("Lista eccezioni"); sb.AppendLine(separator); sb.AppendFormat("Classe: {0} Metodo {1}", pClassName, pMethod.Name); sb.AppendLine(); sb.AppendLine(separator); if (ex.InnerException != null) { Exception inner = ex.InnerException; Stack<string> messageStack = new Stack<string>(); while (true) { messageStack.Push(string.Format("Tipo eccezione: {0}", inner.GetType())); messageStack.Push(inner.Message); messageStack.Push(string.Empty); if (inner.InnerException == null) { break; } inner = inner.InnerException; } sb.AppendLine(separator); sb.AppendLine(); while (messageStack.Count > 0) { sb.AppendLine(messageStack.Pop()); } } sb.AppendLine(separator); sb.AppendFormat("Eccezione pił esterna: {0}", ex.GetType()); sb.AppendLine(); sb.AppendLine(separator); sb.AppendLine(ex.Message); sb.Append("<<"); sb.AppendLine(trattini); return sb.ToString(); } } }'' VB nella libreria TAndT.Base #Region "Imports directives" Imports System Imports System.Collections.Generic Imports System.Reflection Imports System.Text #End Region Public Class ExceptionHelper Private Shared ReadOnly mClassName As String = _ System.Reflection.MethodBase.GetCurrentMethod.ReflectedType.Name Public Shared Function BuildMessage(ByVal className As String, ByVal method As MethodBase, _ ByVal ex As Exception) As String Dim trattini As String = New String("-"c, 80) Dim separator As String = New String("-"c, 10) Dim sb As StringBuilder = New StringBuilder(String.Empty) sb.Append(">>") sb.AppendLine(trattini) sb.AppendFormat("Classe: {0} Metodo {1}", className, method.Name) sb.AppendLine() sb.AppendLine(separator) If Not ex.InnerException Is Nothing Then Dim inner As Exception = ex.InnerException Dim messageStack As New Stack(Of String) Do messageStack.Push(String.Format("Tipo eccezione: {0}", inner.GetType())) messageStack.Push(inner.Message) messageStack.Push(String.Empty) If inner.InnerException Is Nothing Then Exit Do End If inner = inner.InnerException Loop sb.AppendLine(separator) sb.AppendLine() Do While messageStack.Count > 0 sb.AppendLine(messageStack.Pop()) Loop End If sb.AppendLine(separator) sb.AppendFormat("Eccezione pił esterna: {0}", ex.GetType()) sb.AppendLine() sb.AppendLine(separator) sb.AppendLine(ex.Message) sb.Append("<<") Return sb.ToString End Function End ClassExceptionHelper è una classe statica. Cos'è una classe statica? E' una classe non instanziabile, i cui membri sono obbligatoriamente statici (Static o Shared). Non instanziabile non significa inesistente, in verità ogni classe statica viene instanziata automaticamente dal sistema la prima volta che si fa riferimento ad essa. La sua peculiarità è che i suoi membri sono di un'unica instanza (appunto quella automaticamente istanziata di cui sopra) e vengono chiamati con la sintassi NomeClasse.NomeMetodo() invece che NomeInstanza.NomeMetodo().
In Visual Basic 6, un comportamento simile si riscontra in un modulo con tutte le procedure Public. In Visual Basic .Net, una classe statica (Shared) è una classe che ha Shared tutti i suoi membri Public (non necessariamente anche quelli Private).
Nel nostro caso, ExceptionHelper contiene un solo metodo che, pure con una certa sofisticazione, genera una semplice stringa che compone una lista dei messaggi relativi ad una eccezione verificatasi in un punto dell'esecuzione del programma. Se l'eccezione si è verificata in un componente anziché sulla User Interface, si può vedere quale sia l'intera successione delle chiamate a metodo che hanno intercettato le eccezioni che si sono verificate.
Si usa la classe Stack per poter scorrere gli annidamenti delle InnerException delle InnerException delle InnerException... Insomma, consultate l'help! Non possiamo copiarvelo qua.Warnings - Una classe per programmatori pigri
// C# #regionUsing directives using System; using System.Collections.Generic; using System.Reflection; using System.Text; using System.Windows.Forms; using TAndT.Base; #endregion namespace TAndT.UI { public static class Warnings { private static readonly string mClassName = System.Reflection.MethodBase.GetCurrentMethod().ReflectedType.Name; public static void Info( string pMessage ) { MessageBox.Show( pMessage,"Info",MessageBoxButtons.OK,MessageBoxIcon.Information); } public static void Avviso(string pMessage) { MessageBox.Show(pMessage, "Avviso", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); } public static void Errore(string pMessage) { MessageBox.Show(pMessage, "Errore", MessageBoxButtons.OK, MessageBoxIcon.Error); } public static void Errore(string pClassName, MethodBase pMethod, Exception pEx) { string exMessage = ExceptionHelper.BuildMessage(pClassName, pMethod, pEx); MessageBox.Show(exMessage, "Errore", MessageBoxButtons.OK, MessageBoxIcon.Error); } } }'' VB #Region "Imports Directives" Imports System Imports System.Collections.Generic Imports System.Reflection Imports System.Text Imports System.Windows.Forms Imports TAndT.Base #End Region Public Class Warnings Private Shared ReadOnly mClassName As String = _ System.Reflection.MethodBase.GetCurrentMethod.ReflectedType.Name Public Shared Sub Info(ByVal message As String) MessageBox.Show(message, "Info", MessageBoxButtons.OK, MessageBoxIcon.Information) End Sub Public Shared Sub Avviso(ByVal message As String) MessageBox.Show(message, "Avviso", MessageBoxButtons.OK, MessageBoxIcon.Exclamation) End Sub Public Shared Sub Errore(ByVal message As String) MessageBox.Show(message, "Errore", MessageBoxButtons.OK, MessageBoxIcon.Error) End Sub Public Shared Sub Errore(ByVal className As String, ByVal method As MethodBase, _ ByVal ex As Exception) Dim exMessage As String = ExceptionHelper.BuildMessage(className, method, ex) MessageBox.Show(exMessage, "Errore", MessageBoxButtons.OK, MessageBoxIcon.Error) End Sub End ClassDa notare che bisogna aggiungere, al progetto TAndT.UI, al quale appartiene la classe Warnings, un riferimento al progetto TAndT.Base (dal Solution Explorer, Add Reference...Project...)
Questa classe Warnings, anch'essa statica, è predisposta per i pigri come noi: MessageBox è un oggetto utile, ma ha troppi parametri da impostare, perciò, per la gestione messaggi e la gestione errori, abbiamo predisposto delle scorciatioie da chiamare senza doverci ricordare tutti i parametri. Inoltre, se domani mattina decidessimo di non usare una MessageBox standard, ma di crearci una form con i filmini ed i fuochi d'artificio, possiamo cambiare questa classe senza fare il giro di tutti i nostri programmi a sostituire la vecchia MessageBox.FrmMain - La form di test
Visto e considerato che abbiamo scritto due classi e siamo bravi, iniziamo a sviluppare la nostra form di test, FrmMain, creando le funzioni per provare queste due classi.
Dal Solution Explorer, come sopra, aggiungiamo un riferimento a tutti e tre i progetti libreria di classe.A questo punto, per iniziare le prove, trasciniamo sulla nostra form MDI un componente MenuStrip, denominiamolo mnuMain, con l'editor del designer generiamo un menu File, dentro a cui inseriremo una opzione Exit; accanto a File generiamo un menu Helper Tests, in cui inseriamo una opzione Test Warnings Class. Visual Studio, che è intelligente, ci produrrà una serie di MenuItems con un nome intelligente e noi implementeremo immediatamente gli event handler per i click delle due opzioni di menu.
// C# #region Dichiarazione dei namespaces using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; using TAndT.UI; using TAndT.Base; using TAndT.Base.Entities; using TAndT.Base.Collections; #endregion namespace TAndT.UsingSqlServer { public partial class FrmMain : Form { private readonly static string mClassName = System.Reflection.MethodBase.GetCurrentMethod().ReflectedType.Name; public FrmMain() { InitializeComponent(); this.Icon = Properties.Resources.btn359; } private void exitToolStripMenuItem_Click(object sender, EventArgs e) { Application.Exit(); } private void testWarningsClassToolStripMenuItem_Click(object sender, EventArgs e) { try { Warnings.Info("Messaggio di informazione"); Warnings.Avviso("Messaggio di avviso"); Warnings.Errore("Messaggio di errore"); throw new ApplicationException( "Questo messaggio gestisce i dati di una eccezione intercettata"); } catch (Exception ex) { Warnings.Errore(mClassName, System.Reflection.MethodBase.GetCurrentMethod(), ex); } } }'' VB #Region "Import Directives" Imports System Imports System.Collections.Generic Imports System.ComponentModel Imports System.Data Imports System.Drawing Imports System.Text Imports System.Windows.Forms Imports TAndT.UI Imports TAndT.Base #End Region Partial Public Class FrmMain Private Shared ReadOnly mClassName As String = _ System.Reflection.MethodBase.GetCurrentMethod.ReflectedType.Name Private Sub ExitToolStripMenuItem_Click( _ ByVal sender As System.Object, ByVal e As System.EventArgs) _ Handles ExitToolStripMenuItem.Click Application.Exit() End Sub Private Sub TestWarningsClassToolStripMenuItem_Click( _ ByVal sender As System.Object, ByVal e As System.EventArgs) _ Handles TestWarningsClassToolStripMenuItem.Click Try Warnings.Info("Messaggio di informazione") Warnings.Avviso("Messaggio di avviso") Warnings.Errore("Messaggio di errore") Throw New ApplicationException( _ "Questo messaggio gestisce i dati di una eccezione intercettata") Catch ex As Exception Warnings.Errore(mClassName, System.Reflection.MethodBase.GetCurrentMethod, ex) End Try End Sub End ClassCome possiamo notare, nella nostra classe form, abbiamo implementato due event handler per il clic delle nostre opzioni di menu. Visualizziamo anche la forma che assumerà la form a questo punto:
A parte aver aggiunto un'icona e due menu, non sembra abbiamo fatto un gran che... Se però apriamo il menu Helper Test e selezioniamo l'opzione inserita, otterremo quattro diverse message box:
Abbiamo ottenuto le MessageBox semiautomatiche per le funzionalità base di avviso agli utenti; più avanti, se necessario, potremo aggiungere ad esempio un messaggio con conferma YesNo o altro.
Arrivederci alla prossima puntata
Mentre stenderemo il codice per le classi di cui gli articoli discuteranno, ove necessario interromperemo la spiegazione della classe corrente, inserendo il codice, la spiegazione ed eventualmente il test per le funzioni ausiliarie (i cosiddetti Helper) che andranno a comporre una serie di classi, riutilizzabili poi in molti punti del codice.
Non faremo articoli separati per questi oggetti, perché, per quanto spezzino la discussione di una classe, troviamo sia più semplice e utile crearli e spiegarli al primo uso all'interno di una classe, piuttosto che scrivere un articolo che mostri in modo sterile classi, oggetti e metodi che, per quanto interessanti, sono slegate dal loro futuro uso.
Inoltre questo modo di lavorare è molto più vicino alla realtà del lavoro del programmatore, che non stende mai linearmente una singola classe per volta, ma, usualmente, lavora parallelamente su una classe principale e su una serie di classi accessorie.
Insomma, siete ormai avvisati circa i nostri difetti e sapete cosa vi aspetta.
Quindi, controllate nel codice dell'esempio che potete scaricare dall'area Download se avete seguito a puntino le nostre indicazioni e... "Stay Tuned!".