Press "Enter" to skip to content

Common Libraries – Implementare una Window WPF con funzionalità estese

In questo post, vedremo come WPF ci permetta di derivare le proprie classi per permetterci di aggiungere funzionalità anche quando non vogliamo modificare l’aspetto visuale della classe WPF. In questo caso, avendo introdotto nel post precedente un sistema di memorizzazione di parametri di stato per la User Interface delle nostre applicazioni, vogliamo estendere la Window WPF e creare una nuova Window che sappia automaticamente come salvare la propria dimensione. Fatto questo, useremo questa nuova window come classe base per la MainWindow di MiniSqlAgentConsole.

Le modifiche alla libreria DnwBaseWpf

mini_sql_agent_06_solution_03

Per aggiungere la nuova window, creiamo una classe nel progetto della libreria base WPF, non usiamo il Template WPF Window ma il template per le Classi, perché questa classe non avrà alcun file XAML collegato.

public partial class DnwBaseWindow : Window
{
    public string SizeSettingName
    {
        get;
        set;
    }

    public DnwAutoSettingsManagerBase AutoSettings
    {
        get;
        set;
    }

La dichiarazione della classe e le property che serviranno alla classe padre per effettuare il suo lavoro e dovranno essere inizializzate perché il salvataggio della dimensione della finestra venga correttamente effettuato.

protected override void OnInitialized(EventArgs e)
{
    base.OnInitialized(e);
    if (AutoSettings != null && !SizeSettingName.XDwIsNullOrTrimEmpty())
    {
        string size = AutoSettings.GetSetting(SizeSettingName);
        if (size != null)
        {
            string[] elem = size.Split(';');
            if (elem.Length == 2)
            {
                double width = this.Width;
                double.TryParse(elem[0], out width);
                this.Width = width;
                double height = this.Height;
                double.TryParse(elem[1], out height);
                this.Height = height;
            }
        }
    }
}

L’override dell’evento Initialized della Window, dove, se sono stati inizializzati i valori delle property, viene recuperato il valore delle dimensioni della finestra ed assegnato alle property WIdth ed Height.

protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
{
    base.OnClosing(e);
    if (AutoSettings != null && !SizeSettingName.XDwIsNullOrTrimEmpty())
    {
        AutoSettings.SetSetting(SizeSettingName, string.Format("{0};{1}", this.Width, this.Height));
    }
}

L’override dell’evento Closing della Window, dove, se sono stati inizializzati i valori delle property, viene salvata la dimensione della finestra.

protected override void OnClosed(EventArgs e)
{
    AutoSettings = null;
    SizeSettingName = null;
    base.OnClosed(e);

}

L’override dell’evento Closed della Window, dove forziamo il rilascio delle due property per evitare che se i due oggetti sono globali nell’applicazione provochino il persistere di riferimenti e quindi memory leaks.

Le modifiche alla classe MainWindow in MiniSqlAgentConsole

public partial class MainWindow : DnwBaseWindow

Prima modifica, deriviamo la classe dalla nuova Window da noi creata.

<gwin:DnwBaseWindow 
    x:Class="Dnw.MiniSqlAgent.Console.Windows.MainWindow"
    xmlns:gwin ="clr-namespace:Dnw.Base.Wpf.Windows;assembly=Dnw.Base.Wpf.v4.0"
....
    <gwin:DnwBaseWindow.Resources>
....
    </gwin:DnwBaseWindow.Resources>
    <gwin:DnwBaseWindow.CommandBindings>
....
    </gwin:DnwBaseWindow.CommandBindings>
....
</gwin:DnwBaseWindow>

Modifichiamo lo XAML per derivare correttamente dalla nuova Window e sostituiamo Window con DnwBaseWindow in tutte le ricorrenze nello XAML.

protected override void OnStartup(StartupEventArgs e)
{
......
    base.OnStartup(e);

    FrameworkElement.StyleProperty.OverrideMetadata(typeof(Window),  new FrameworkPropertyMetadata(
            TryFindResource(typeof(Window))));  ......
}

Modifichiamo l’OnStartup nella classe App.xaml.cs in modo da forzare l’applicazione del Template e degli Stili di default agli oggetti Window, è un pattern consigliato per essere sicuri che l’aspetto delle window derivate sia corretto, non siamo certi sia obbligatorio farlo, ma essendo indicato su MSDN lo abbiamo fatto.

A questo punto siamo quasi pronti, c’è solo da aggiungere il valore alle property necessarie alla classe padre per compiere il suo lavoro:

protected override void OnInitialized(EventArgs e)
{
    //Put this before the call to base.OnInitialized
    //Because that is the method that uses the parameters to set
    //the window size
    this.AutoSettings = AutoSettingsManager.Instance;
    this.SizeSettingName = string.Format("{0}Size", typeof(MainWindow).Name);
    base.OnInitialized(e);

}

Importante chiamare l’OnInitialized della classe padre DOPO aver inizializzato le property altrimenti la classe padre non è in grado di rileggere il dato salvato  perché non sa dov’è.

Il codice del progetto di esempio relativo alle nuove librerie comuni è disponibile al link seguente:

Il codice di MiniSqlAgent aggiornato con la nuova window è disponibile al link seguente:

Per qualsiasi domanda, approfondimento, o curiosità, utilizzate pure il link alla form di contatto in cima alla pagina o il forum Microsoft Italia su cui rispondo regolarmente.