Impostazioni di applicazione in Visual Basic Net
a cura di Diego Cattaruzza (requisiti: Framework 2.0)Premessa
Una questione che viene posta in modo ricorrente riguarda la gestione delle impostazioni di programma, cioè l'organizzazione di informazioni variabili che si vuole siano persistenti tra una esecuzione e l'altra del programma.
Chi ha già sviluppato con Visual Basic 6 si ricorda di aver gestito questa cosa mediante file di inizializzazione (normali file di testo, con estensione .ini, strutturati in sezioni contenenti elenchi di coppie chiave-valore, da leggere o scrivere attraverso apposite API), oppure tramite le istruzioni o funzioni Get/Save/DeleteSetting che agivano in una apposita sezione del registro di configurazione di Windows, pratica assolutamente sconsigliata, su questo sito - ragion per cui non ne se ne leggerà nel seguito.
Per facilitare l'uso delle API dedicate ai file di inzializzazione, i programmatori più avvertiti avranno anche sviluppato delle classi apposite che avvolgevano le funzioni di sistema (proprio questa funzionalità è argomento del mio articolo 'La mia prima classe').Come si fa in Visual Basic Net? Questo articolo si propone di spiegarlo, con ulteriori considerazioni.
Application Settings
Tutte le configurazioni (di quasi qualsiasi cosa), in Net, sono registrate in appositi file XML di configurazione, secondo lo schema descritto nel namespace System.Configuration.
In VB.Net ogni applicazione creata con Visual Studio ha i suoi file app.config e Settings.settings, che vengano o meno usati.Sono file XML strutturati che possono essere assimilati ai noti file .ini, con la differenza che le coppie chiave-valore
[Sezione] chiave=valoresono sostituite da una gerarchia elemento-valore:
<sezione.Settings> <setting name="chiave" tipochiave=""> <value>valore</value> </setting> </sezione.Settings>Un'altra differenza tra la configurazione 'ini-com' e la configurazione 'xml-net' è che quest'ultima è fortemente tipizzata (la parola tipochiave, che non esiste, serve solo a indicare questa ulteriore caratteristica). Naturalmente, è possibile creare diverse sezioni, con impostazioni per l'applicazione o per l'utente.
Con VB.Net è possibile approfittare delle funzionalità offerte in diverse maniere, dalla più comoda alla più manuale, mentre in VB6 si deve scrivere tutto il codice.
La via più comoda
Da Visual Studio si può impostare la persistenza di proprietà di una Form o di un controllo direttamente dalla finestra proprietà. Seguite questo esempio per persistere il colore di fondo di una Form. L'utente avrà la possibilità di scegliere il proprio colore preferito attraverso il classico dialogo.
- Aprite Visual Basic (io uso Visual Basic 2008 Express, ma vale anche per il 2005)
- create un nuovo progetto, di tipo Windows Form Application, di nome IniSettingsStudio
- della Form1, modificate Name (FrmMain) e Text ("Studio Ini Settings")
- Aggiungete un Button sulla Form
- Name = cmdChangeBackColor
- AutoSize = True
- BackColor = (scegliete un colore chiaro a vostro piacere, basta che sia diverso da quello di default)
- Text = "Colore di sfondo"
- Aggiungete un componente ColorDialog, Name = dlgColor, FullOpen = True
- Selezionate la FrmMain, per visualizzarne, di nuovo, le proprietà:
- Espandete la struttura di ApplicationSettings
- Scegliete PropertyBinding e fate clic sui puntini che compaiono
- Scegliete BackColor ed espandete la relativa casella combinata
Appare vuota (viene proposta la scelta None, poiché non esiste ancora alcuna impostazione, ma avete la possibilità di crearne una nuova)- Fate clic su New...
- Lasciate invariato il valore di default (DefaultValue)
- Digitate il nome per l'impostazione: Name = frmMainBackColor
- Lasciate invariato l'ambito di validità dell'impostazione: Scope = User
- Fate clic su Ok per confermare la creazione dell'impostazione.
- Fate doppio clic sul pulsante cmdChangeBackColor per accedere all'editor di codice, sull'evento Click, e digitate questo frammento (così vedete come Intellisense propone o no il completamento):
Private Sub cmdChangeBackColor_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _ Handles cmdChangeBackColor.Click dlgColor.Color = My.Settings.FrmMainBackColor Dim dr As DialogResult = dlgColor.ShowDialog() If dr = Windows.Forms.DialogResult.OK Then Me.BackColor = dlgColor.Color End If MessageBox.Show(My.Settings.FrmMainBackColor.ToString, "frmMainBackColor") End SubQuesto codice usa la facilitazione fornita dal namespace My (tipico delle applicazioni Visual Basic), che espone l'oggetto Settings, il quale espone le impostazioni dell'applicazione, comprese quelle create dallo sviluppatore.
Il codice prepara la finestra di dialogo per la scelta del colore di fondo della form, informandola dell'attuale impostazione, ossia impostando la proprietà Color della dlgColor con il valore della impostazione frmMainBackColor, quindi attiva il dialogo e ne riceve il risultato, se questo è OK imposta il colore di fondo della FrmMain al colore impostato dalla finestra di dialogo; in ogni caso, mostra l'attuale valore dell'impostazione frmMainBackColor, la quale rimane la stessa oppure muta, a seconda dell'esito del dialogo.Compilate e avviate. Fate clic sul pulsante e cambiate il colore. Chiudete e riavviate, vedrete che la Form ha ripreso l'ultimo colore impostato. Abbiamo quindi ottenuto la persistenza dell'impostazione.
Dietro le quinte
Nella finestra Solution Explorer, se non l'avete ancora fatto per abitudine, fate clic sull'icona per visualizzare tutti i file (Show All Files), espandete My Project e aprite Settings.settings.Potete vedere l'impostazione di nome frmBackColor, di tipo Color, valida per l'utente, il cui valore di default è il colore Control. Questa è l'impostazione a livello di progettazione.
Quando viene creata una qualsiasi impostazione, Visual Studio crea il file app.config, che, tra le altre cose, contiene la dichiarazione del gruppo di sezioni userSettings, con una sola sezione, IniSettingsStudio.My.MySettings:<configSections> <sectionGroup name="userSettings" type="System.Configuration.UserSettingsGroup, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" > <section name="IniSettingsStudio.My.MySettings" type="System.Configuration.ClientSettingsSection, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowExeDefinition="MachineToLocalUser" requirePermission="false" /> </sectionGroup> </configSections>nella quale troviamo l'impostazione appena creata e provata:
<userSettings> <IniSettingsStudio.My.MySettings> <setting name="frmMainBackColor" serializeAs="String"> <value>Control</value> </setting> </IniSettingsStudio.My.MySettings> </userSettings>Qui però il valore è ancora Control, cioè il valore di default; dove sta il valore modificato, che persiste tra una sessione e l'altra di esecuzione del programma? Sta in un file di nome user.config, che si trova, secondo una affrettata lettura dell'help, nella cartella Impostazioni locali\Dati applicazioni\ dell'utente e precisamente nella cartella definita dalla proprietà LocalUserAppDataPath dell'oggetto Application.
Per facilitare la ricerca di questa informazione, aggiungete un Button (Name = cmdInfo, Text = "Mostra Info"), e digitate nel gestore del suo evento Click il seguente codice:Private Sub cmdInfo_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _ Handles cmdInfo.Click Dim cr As String = Environment.NewLine MessageBox.Show(String.Format("{0}: {1}{2}{3}: {4}{5}", _ "User.Name", My.User.Name, cr, _ "LocalUserAppDataPath" & cr, Application.LocalUserAppDataPath, cr, _ , "Informazioni Utente") End SubIl codice si limita a predisporre una variabile il cui nome è costituito da soli due caratteri, per usarla nella costruzione di un messaggio tramite il metodo statico Format della classe String, che riceve come parametri la stringa di formato coi segnaposto, e gli elementi da introdurre in quei segnaposto.
Lanciate il programma e fate clic sul pulsante cmdInfo. Tenendo aperta la finestra di dialogo, aprite Gestione Risorse e cercate la cartella indicata.
Sorpresa: non solo la cartella è vuota, ma ce ne sono altre molto simili nel nome, con assurdi finali criptici, in ciascuna delle quali c'è un file user.config!
E' certamente una caratteristica voluta, per scopi di sicurezza e riservatezza, per quanto si sconsigli di usare user.config per archiviarvi dati 'segreti', se non criptati in qualche modo.Tornando alla nostra indagine: qual è il file giusto? Si intuisce che ce ne sono diversi perché sono stati diversi, nel tempo, gli assembly avviati dopo ogni nostra modifica. Si può presumere che nella realtà dell'uso ce ne sarà sempre solo uno, ma se si volesse essere sicuri di trovare quello relativo all'attuale versione del programma?
Sapete già che i file di configurazione seguono lo schema definito nel Namespace System.Configuration (ve l'ho scritto all'inizio). Cercate in esso se trovate qualcosa che fa al caso nostro. Così troverete la classe omonima, che espone una proprietà FilePath. Bisogna ottenere una istanza di Configuration attraverso uno dei metodi forniti dalla classe ConfigurationManager, che è statica, cioè non è necessario produrne un'istanza per usarne un metodo.
Scoperte queste informazioni, aggiungete al progetto il riferimento al Namespace System.Configuration e implementate il seguente metodo:Private Function UserConfigFilePath() As String Dim MyConfiguration As Configuration = _ ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.PerUserRoamingAndLocal) Return MyConfiguration.FilePath End FunctionPoi modificate il codice del cmdInfo_Click:
Private Sub cmdInfo_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _ Handles cmdInfo.Click Dim cr As String = Environment.NewLine MessageBox.Show(String.Format("{0}: {1}{2}{3}: {4}{5}{6}: {7}{8}", _ "User.Name", My.User.Name, cr, _ "LocalUserAppDataPath" & cr, Application.LocalUserAppDataPath, cr, _ "UserConfigFilePath" & cr, UserConfigFilePath, cr) _ , "Informazioni Utente") End SubPotete così andare ad aprire questo file e trovarvi il valore attuale dell'impostazione, qualcosa di simile a:
<configuration> <userSettings> <IniSettingsStudio.My.MySettings> <setting name="frmMainBackColor" serializeAs="String"> <value>221, 238, 238</value> </setting> </IniSettingsStudio.My.MySettings> </userSettings> </configuration>Il file app.config, invece, espone chiavi e valori che possono soltanto essere letti. Le impostazioni attinenti l'Application, sono stabilite in sede di progettazione, quelle relative all'utente generico fungono da indicazioni statiche dei valori di default.
La via un po' meno comoda
Come è logico, si può implementare una impostazione seguendo la strada inversa. Può succedere infatti che si abbia in mente di voler gestire una impostazione prima ancora di aver deciso come farlo.
Immaginiamo che ci serva conservare nickname e indirizzo email dell'utente, ad esempio per fornire queste info nelle richieste di assistenza.
Quindi aprite il file Setting.settings e aggiungete queste impostazioni come indicato in figura:![]()
Queste impostazioni possono essere richieste in fase di installazione, per esempio, oppure nel momento in cui viene richiesta assistenza, oppure in seguito alla scelta di un apposito menu.
A ogni modo, costruiamo una form di dialogo apposita:
Fate clic destro sul progetto e aggiungete una Windows Form di tipo Dialog, dandogli il nome DlgUser. Modificatene il titolo in "Impostazioni account utente".
Aggiungete tre Label (con Text rispettivamente "Utente", "NickName" e "Email") e tre Textbox (txtUser, txtNickName e txtEmail); impostate a True la proprietà ReadOnly di txtUser e disponete il tutto come in figura:![]()
Adesso selezionate la casella txtNickName, quindi nella finestra Proprietà selezionate ApplicationSettings, PropertyBindings, 'puntini'; nella finestra così apertasi selezionate la proprietà Text e vedrete che potete scegliere dall'elenco l'impostazione usrNickName. Lo stesso potrete fare per associare la proprietà Text della casella txtEmail all'impostazione usrEmail.
Notate come vengano proposte nell'elenco solo le impostazioni di tipo congruente con la proprietà che si vuole associare (per una proprietà di tipo String, non viene proposta anche frmMainBackColor, che è di tipo Color).A questo punto bisogna gestire il contenuto di txtUser e la validità della modifica delle impostazioni, anche in conseguenza del clic sul pulsante OK o sul pulsante Cancel. Infatti, ciò che fanno 'automaticamente' questi pulsanti è solo restituire al chiamante un DialogResult specifico. Il resto deve essere gestito dal codice.
Quindi impostate il contenuto della txtUser al caricamento della Form, e nel contempo depositate le attuali impostazioni:Private Sub DlgUser_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) _ Handles MyBase.Load Me.txtUser.Text = My.User.Name Me.txtNickName.Tag = Me.txtNickName.Text Me.txtEmail.Tag = Me.txtEmail.Text End SubE curate il ripristino delle impostazioni precedenti alle eventuali modifiche, nel caso si faccia clic sul pulsante Cancel:
Private Sub Cancel_Button_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _ Handles Cancel_Button.Click Me.txtNickName.Text = Me.txtNickName.Tag Me.txtEmail.Text = Me.txtEmail.Tag Me.DialogResult = System.Windows.Forms.DialogResult.Cancel Me.Close() End SubIn questa sede, non ci si occupa di controllare la validità e l'accettabilità dei dati immessi, come si dovrebbe fare in una applicazione non di studio.
Infine, nella FrmMain, aggiungete un pulsante (Name = cmdUserProfile, Text = "Profilo utente") per verificare e studiare questo comportamento:Private Sub cmdUserProfile_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _ Handles cmdUserProfile.Click Dim du As New DlgUser du.ShowDialog() du = Nothing End SubPoiché è 'faticoso' andarsi a cercare il file user.config per verificare i cambiamenti, facciamo in modo che tale fatica la svolga il nostro programmino di studio.
Aggiungete un'altra finestra di dialogo, (Name = DlgFileView, Text = File user.config), eliminate il tasto Cancel e il TableLayoutPanel1, dopo aver spostato il tasto OK. Aggiungete una Textbox (Name = txt, Multiline = True, ScrollBars = Both). Sistemate dimensioni e posizioni dei controlli, in modo che la casella occupi quasi tutta la finestra di dialogo e che il pulsante stia in basso a destra, fino a ottenere pressappoco un risultato come in figura:![]()
Questa form deve conoscere il percorso del file user.config, per trovare il quale si usa già un metodo che non ha senso ricopiare: implementate quindi un overload del metodo New, che riceva quel dato come parametro:
Private mPath As String Public Sub New(ByVal path As String) InitializeComponent() mPath = path End SubLa lettura va fatta nel metodo di gestione dell'evento Load:
Private Sub DlgFileView_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) _ Handles MyBase.Load txt.Text = My.Computer.FileSystem.ReadAllText(mPath) End SubA questo punto, aggiungete alla FrmMain un altro pulsante (Name = cmdViewUserConfig, Text = "Visualizza user.config") e implementate questo codice per il suo evento Click:
Private Sub cmdViewUserConfig_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _ Handles cmdViewUserConfig.Click Dim df As New DlgFileView(UserConfigFilePath) df.ShowDialog() df = Nothing End SubPotrete così vedere direttamente il contenuto del file user.config con le ultime impostazioni inserite:
![]()
La via più diretta
L'esame dei file .config permette di ipotizzare e sperimentare la creazione di nuove impostazioni senza far uso del designer.
Aprite il file app.config e aggiungete un nodo setting:<setting name="frmDlgUserText" serializeAs="String"> <value>Impostazioni account utente</value> </setting>Aprite il file Settings.Designer.vb e trovate la classe MySettings, cui aggiungete, in coda alle altre, la proprietà che definisce la nuova impostazione:
<Global.System.Configuration.UserScopedSettingAttribute(), _ Global.System.Diagnostics.DebuggerNonUserCodeAttribute(), _ Global.System.Configuration.DefaultSettingValueAttribute("Impostazioni account utente")> _ Public Property frmDlgUserText() As String Get Return CType(Me("frmDlgUserText"), String) End Get Set(ByVal value As String) Me("frmDlgUserText") = value End Set End PropertyAprite la form DlgUser, modificate, a scopo di successiva verifica, la proprietà Text con una parola qualsiasi, e impostate l'ApplicationSettings/Text alla nuova impostazione, il cui nome trovate nell'elenco, come forse vi aspettavate.
Ciò produce, nel metodo InitializeComponent della DlgUser (nel file DlgUser.Designer.vb) la seguente riga di codice:Me.Text = Global.IniSettingsStudio.My.MySettings.Default.frmDlgUserTextL'impostazione creata nell'app.config serve da 'traccia' per l'analoga impostazione presente nello user.config, che va gestito nel primo avvio dell'applicazione:
Private Sub FrmMain_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load If My.Settings.frmDlgUserText.Contains("utente") Then My.Settings.frmDlgUserText = My.Settings.frmDlgUserText.Replace("utente", My.User.Name) End If End SubSe l'impostazione frmDlgUserText ha ancora il valore di default, contiene la parola 'utente', quindi, sostituendola con il nome dell'utente, viene corretta l'impostazione.
Avviamo il programma e verifichiamo che tutto si svolge come previsto.Conclusioni
Tutto questo ci aiuta a capire come funziona, anche se è poco probabile che davvero serva a qualcuno avere un accesso al file diverso da quello agevolmente permesso dalle vie più o meno comode offerte dall'IDE.Finora abbiamo constatato che possiamo rimpiazzare i vecchi file .ini con i nuovi .xml, app.config e user.config, con molta maggior efficacia. Almeno per le impostazioni meno complesse.
In un prossimo articolo vedremo come impostare una configurazione completamente sotto il nostro controllo, per i casi in cui gli automatismi offertici dall'IDE non siano soddisfacenti, per quanto comodissimi.Come al solito, il codice a corredo di questo articolo si trova in area download, e potete scrivere al mio indirizzo per chiarimenti, critiche o suggerimenti.