Considerazioni sulla gestione delle impostazioni di applicazioni WinForm tramite PropertyGrid
In questi giorni a tempo perso sto sviluppando l’applicazione .NET WinForms PowerTray che ha l’obbiettivo di poter eseguire script PowerShell e di visualizzare l’output quando lo si desidera tramite una un’icona nella tray bar. L’obbiettivo di PowerTray, attualmente in sviluppo, è quello di implementare funzionalità simili a BgInfo, ma con una maggiore flessibilità nell’aggiornamento dell’output, e dare la possibilità di eseguire script PowerShell in moto automatizzato o manuale.
Durante lo sviluppo di tale applicazione ho avuto la necessita di gestire le impostazioni della stessa. In ambito WinForms se le impostazioni sono gestite tramite una classe si possono avere due approcci ovvero gestire le impostazioni tramite una form dedicata su cui vi sono i vari controlli per l’impostazione dei vari settaggio oppure gestire l’oggetto della classe impostazioni tramite un PropertyGrid e serializzare poi l’oggetto su un file XML o Json.
Di seguito a futura memoria e per l’eventuale utilità di chi si può trovare ad analizzare questo tipo di decisioni.
Considerazione 1:
Se le impostazioni non sono complesse il PropertyGrid può realizzare una interfaccia grafica semplice e intuiva sgravando l’onere di sviluppare form con vari controlli. Di seguito un elenco di suggerimenti che possono velocizzare la personalizzazione della visualizzazione e delle impostazioni e la loro impostazione:
- Il nome dell’impostazione sarà il nome della relativa proprietà, ma e è possibile personalizzare tale nome decorando la proprietà con l’attributo System.ComponentModel.DisplayName, inoltre è possibile visualizzare una descrizione decorando la proprietà con l’attributo System.ComponentModel.Description.
- E’ possibile raggruppare le impostazioni decorando le relative proprietà con l’attributo System.ComponentModel.Category.
Considerazione 2:
E’ possibile gestire l’editing delle proprietà utilizzando o sviluppando editor specifici. Per esempio se si desidera che un’impostazione che fa riferimento ad una proprietà stringa visualizzi come editor una dialog che permetta di selezionare un file è possibile decorare la proprietà con l’attributo System.ComponentModel.Editor:
<System.ComponentModel.Editor(GetType(System.Windows.Forms.Design.FileNameEditor), GetType(System.Drawing.Design.UITypeEditor))>
Se si intende avere un editor più specifico o personalizzare un edito è necessario implementare una propria classe di editor. Ad esempio se si desidera visualizzi come editor una dialog che permetta di selezionare esclusivamente un file .ps1 è possibile implementare la seguente classe:
Public Class PSFileNameEditor
Inherits System.Windows.Forms.Design.FileNameEditor
Protected Overrides Sub InitializeDialog(openFileDialog As OpenFileDialog)
MyBase.InitializeDialog(openFileDialog)
openFileDialog.DefaultExt = “ps1”
openFileDialog.Filter = “PowerShell File|*.ps1”
Me.GetEditStyle()
End Sub
Public Overrides Function GetEditStyle(context As ITypeDescriptorContext) As UITypeEditorEditStyle
If context IsNot Nothing AndAlso context.PropertyDescriptor.IsReadOnly Then
Return UITypeEditorEditStyle.None
End If
Return UITypeEditorEditStyle.Modal
End Function
End Class
In questo caso la proprietà andrà poi decorata con l’attributo System.ComponentModel.Editor specificando l’editor personalizzato:
<System.ComponentModel.Editor(GetType(PSFileNameEditor), GetType(System.Drawing.Design.UITypeEditor))>
Considerazione 3:
E’ possibile gestire il valore di default dell’impostazione decorando la relativa proprietà tramite l’attributo System.ComponentModel.DefaultValue, o nei casi di valori più complessi ( ad esempio strutture) tramite il metodo ShouldSerialize (a riguardo si veda Defining Default Values with the ShouldSerialize and Reset Methods). Per visualizzare nella PropertyGrid un menu contestuale che permetta di resettare al valore di default la proprietà occorre utilizzare un codice di questo tipo:
Private Sub cmnPropertyGrid_Opening(sender As Object, e As System.ComponentModel.CancelEventArgs) Handles cmnPropertyGrid.Opening
Dim menu = DirectCast(sender, System.Windows.Forms.ContextMenuStrip)
Dim propertyGrid = DirectCast(menu.SourceControl, System.Windows.Forms.PropertyGrid)
If propertyGrid.SelectedGridItem.PropertyDescriptor IsNot Nothing Then
Dim canResetItem = propertyGrid.SelectedGridItem.PropertyDescriptor.CanResetValue(propertyGrid.SelectedObject)
Me.cmiPropertyGridReset.Enabled = canResetItem
End If
End Sub
Private Sub cmiPropertyGridReset_Click(sender As Object, e As EventArgs) Handles cmiPropertyGridReset.Click
Dim menuItem = DirectCast(sender, System.Windows.Forms.ToolStripMenuItem)
Dim menu = DirectCast(menuItem.Owner, System.Windows.Forms.ContextMenuStrip)
Dim propertyGrid = DirectCast(menu.SourceControl, System.Windows.Forms.PropertyGrid)
propertyGrid.SelectedGridItem.PropertyDescriptor.ResetValue(propertyGrid.SelectedObject)
propertyGrid.Refresh()End Sub
Considerazione 4:
Se le impostazioni prevedono tra le impostazioni degli oggetti che sono a loro volta una collezione di oggetti il PropertyGrid visualizza come editor un System.ComponentModel.Design.CollectionEditor che presenta alcune limitazioni, ad esempio non visualizza le descrizioni delle proprietà impostate tramite l’attributo System.ComponentModel.Description. In questo caso potrebbe essere più semplice gestire tali collezioni tramite un secondo PropertyGrid, di seguito la soluzione che ho adottato in PowerTray:
Considerazione 5:
Se diventa necessario gestire impostazioni “dinamiche” diventa necessario sviluppare codice ad hoc per implementare tali funzionalità tenendo conto che le impostazioni vengono gestite sulla base dell’interpretazione dell'”oggetto impostazioni” da parte del PropertyGrid.
Per personalizzare l’impostazione di una proprietà restringendo la scelta del valore ad un set predefinito di valori è possibile implementare un TypeConverter personalizzato, nei prossimi giorni pubblicherò un post a riguardo.
Per disabilitare o abilitare talune impostazioni in base a come sono state impostate altre impostazioni diventa necessario gestire dinamicamente gli attributi a runtime, nei prossimi giorni pubblicherò un post a riguardo.
Conclusioni
L’uso di un PropertyGrid per la gestione delle impostazioni può essere molto pratico se le impostazioni sono semplici o se si ha dimestichezza con la gestione di attributi, editor e TypeConverter. In caso contrario con impostazioni che prevendono una certa complessità d’interfaccia o che potrebbero prevederla con l’evoluzione dell’applicazione potrebbe essere più pratico sviluppare una form dedicata per la gestione delle impostazioni.
[…] avevo scritto nel post Considerazioni sulla gestione delle impostazioni di applicazioni WinForm tramite PropertyGrid talvolta per applicazioni WindowsForms può essere comodo utilizzare un PropertyGrid per […]