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:

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.