Press "Enter" to skip to content

5 – MultiClock – Aggiungere la possibilità di configurare dimensione e font di ogni orologio

Considerato che come in ogni applicazione che si rispetti, quando diamo agli utenti una nuova funzionalità un altra viene richiesta subito dopo, perché “Beh, già che ci sei perché…” una volta che abbiamo consegnato ai nostri utenti la versione 3.0 dell’orologio, che permetteva di configurare il colore di ogni orologio, i miei utenti hanno immediatamente detto: “Bellissimo, ma…”

Perché non ci permetti anche di decidere quanto vogliamo che l’orologio sia grande e con quale font vogliamo che sia visualizzato.

clock_font_and_size_01

E quindi questo

clock_font_and_size_02

o questo, sono due esempi del risultato da raggiungere.

Le modifiche da effettuare alle nostre classi sono le seguenti:

  1. Aggiungere a ClockInfo la property ClockFontSize, di tipo double che ospiterà la dimensione della font dell’orologio.
  2. Aggiungere a ClockInfo quattro property calcolate di tipo double che restituiscono rispettivamente:
    • Dimensione del nome dell’orologio
    • Dimensione della visualizzazione dello stato del Daylight saving time
    • Dimensione della data
    • Dimensione della Time Zone
  3. Aggiungere a ClockInfo la property ClockFontFamily di tipo FontFamily che ospiterà il tipo di font da utilizzare per visualizzare l’orologio.
  4. Aggiornare i metodi Copy e CompareTo aggiungendo le nuove property.
  5. Modificare ClockControl, in modo che gli attributi FontSize e FontFamily dei controlli che visualizzano l’orologio siano in Binding con le nuove property generate.
  6. Modificare SetClocksWindow aggiungendo due controlli per poter impostare FontSize e Font Family, ovvero un DoubleUpDown preso dalla libreria dell’ Extanded Wpf Toolkit e una Combobox per visualizzare e selezionare la font family in base alle font installate sulla macchina.

Correggiamo un Bug

Prima di iniziare, correggiamo un Bug che ho trovato debuggando la nuova versione.

Mentre verificavo il funzionamento delle modifiche, sono andata a cercare il file  in cui salviamo i dati di configurazione dell’applicazione, che nella mia mente doveva essere così chiamato:

C:\Users\MioNomeUtente\Documents\DNW\MultiClocksConfig.json

Andando a cercarlo, mi sono accorta che invece veniva impostato in:

c:\Dnw\MulticlocksConfig.json

A prima vista il codice che compone il nome, che si trova nella classe MultiClocksConfig.cs sembra perfetto:

public static string GetConfigFileName()
{
    return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), FileName);
}

In questa riga di codice non c’è alcun errore, ma a causa di come io ho costruito il valore della costante FileName, viene provocato un errore logico.

private const string FileName = "\\DNW\\MultiClocksConfig.json";

La costante, che io avevo definito aveva questo valore, andando a fare un Path.Combine di questo valore con il nome della cartella Documenti dell’utente, la presenza della barra all’inizio del nome, fa si che il risultato di Path.Combine venga posto sulla root del disco correntemente selezionato. Esattamente dove io ho trovato il file.

La correzione è semplice:

private const string FileName = "DNW\\MultiClocksConfig.json";

Dopo questa correzione, il nome del file viene composto correttamente.

Le modifiche a ClockInfo.cs

public const string FLD_ClockFontFamily = "ClockFontFamily";
private FontFamily mClockFontFamily;
public FontFamily ClockFontFamily
{
    get
    {
        return mClockFontFamily;
    }
    set
    {
        mClockFontFamily = value;
        OnPropertyChanged(FLD_ClockFontFamily);
    }
}

La property che ospita la font family che useremo per gli elementi visuali dell’orologio.

public const string FLD_ClockFontSize = "ClockFontSize";
private double mClockFontSize;
public double ClockFontSize
{
    get
    {
        return mClockFontSize;
    }
    set
    {
        if (value < 10.0)
            mClockFontSize = 10;
        else
            mClockFontSize = value;
        OnPropertyChanged(FLD_ClockFontSize);
        OnPropertyChanged(FLD_NameFontSize);
        OnPropertyChanged(FLD_DSTFontSize);
        OnPropertyChanged(FLD_DateFontSize);
        OnPropertyChanged(FLD_TimeZoneFontSize);
    }
}

La property che ospita la dimensione della font dell’orologio, ma che quando viene cambiata, informa la user interface del fatto che sono cambiate anche le dimensioni del nome, della data, della time zone e della DST. In modo che tutti i controlli possano essere inizializzati correttamente. Abbiamo anche inserito un “fallback” in modo che la dimensione dell’orologio non possa scendere sotto i 10 punti per evitare di avere degli orologi illeggibili.

[JsonIgnore]
[XmlIgnore]
public double DateFontSize
{
    get
    {
        double fs = mClockFontSize * 0.25;
        if (fs < 10.0) fs = 10.0;
        return fs;
    }
}
[JsonIgnore]
[XmlIgnore]
public double DSTFontSize
{
    get
    {
        double fs = mClockFontSize * 0.25;
        if (fs < 10.0) fs = 10.0;
        return fs;
    }
}

[JsonIgnore]
[XmlIgnore]
public double NameFontSize
{
    get
    {
        double fs = mClockFontSize * 0.38;
        if (fs < 10.0) fs = 10.0;
        return fs;
    }
}

[JsonIgnore]
[XmlIgnore]
public double TimeZoneFontSize
{
    get
    {
        double fs = mClockFontSize * 0.22;
        if (fs < 10.0) fs = 10.0;
        return fs;
    }
}

Le property calcolate che creano i valori delle dimensioni dei 4 controlli accessori all’orologio, ognuno di essi è ottenuto rimpicciolendo la font dell’orologio, ma abbiamo aggiunto un controllo che non rende la font più piccola di 10 punti per evitare che diventino illeggibili. Abbiamo anche espressamente indicato che queste property non devono essere serializzate.

public void Copy(ClockInfo item)
{
    item.Name = this.Name;
    item.Order = this.Order;
    item.TimeZone = this.TimeZone;
    item.ForegroundColor = this.ForegroundColor;
    item.BackgroundColor = this.BackgroundColor;
    item.IsPrimaryClock = this.IsPrimaryClock;
    item.ClockFontSize = this.ClockFontSize;
    item.ClockFontFamily = this.ClockFontFamily;
}

Il metodo Copy modificato con l’aggiunta delle due nuove property.

public int CompareTo(object obj)
{
    int comparator = -1;
 
    if (obj is ClockInfo)
    {
        ClockInfo val = (ClockInfo)obj;
        comparator = 0;
 
        comparator = CompareStrings(this.Name, val.Name);
        if (comparator != 0) return comparator;
        comparator = CompareStrings(this.TimeZoneName, val.TimeZoneName);
        if (comparator != 0) return comparator;
        comparator = this.Order.CompareTo(val.Order);
        if (comparator != 0) return comparator;
        comparator = CompareStrings(this.BackgroundColor.ToString(), val.BackgroundColor.ToString());
        if (comparator != 0) return comparator;
        comparator = CompareStrings(this.ForegroundColor.ToString(), val.ForegroundColor.ToString());
        if (comparator != 0) return comparator;
        comparator = this.IsPrimaryClock.CompareTo(val.IsPrimaryClock);
        if (comparator != 0) return comparator;
        comparator = this.ClockFontSize.CompareTo(val.ClockFontSize);
        if (comparator != 0) return comparator;
        if (ClockFontFamily != null && val.ClockFontFamily != null)
        {
            comparator = this.ClockFontFamily.Source.CompareTo(val.ClockFontFamily.Source);
        }
        return (comparator);
    }
 
    return (comparator);
}

Il metodo CompareTo con le nuove property aggiunte. Considerato che FontFamily non ha un metodo CompareTo, utilizziamo la property che restituisce il nome della font family ma ignoriamo la comparazione se i due dati sono nulli.

Le modifiche a ClockControl.xaml

    FontFamily="{Binding Clock.ClockFontFamily}"
   FontSize="{Binding Clock.NameFontSize}"

In tutti i controlli Textblock dello user control, inseriamo il binding alle property corrette appena generate, quindi nell’esempio abbiamo inserito il valore della textblock del nome dell’orologio, nelle altre, indicheremo sempre la ClockFontFamily, ma indicheremo la FontSize più opportuna.

Le modifiche a SetClocksWindow.xaml

        <Grid 
            Grid.Row="1">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
            </Grid.RowDefinitions>

Prima di tutto, aggiungiamo due righe alla grid che ospita il dettaglio dell’orologio corrente.

<CheckBox
    Grid.Row="6"

Spostiamo la checkbox che decide qual’è il primary clock in basso di 2 righe.

            <TextBlock
                Grid.Row="4"
                Grid.Column="0"
                Margin="4,2,4,2"
                HorizontalAlignment="Right"
                VerticalAlignment="Center"
                Text="Clock Font Size"/>
            <wpfx:DoubleUpDown 
                Grid.Row="4"
                Grid.Column="1"
                VerticalAlignment="Center"
                HorizontalAlignment="Stretch"
                Height="30" 
                Value="{Binding SelectedClock.ClockFontSize, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
                AllowTextInput="True"
                AllowSpin="True"
                Increment="0.2"
                Margin="4,2,4,2" />

Aggiungiamo a riga 4 La textblock e la DoubleUpDown per acquisire la dimensione della font. DoubleUpDown è un altro dei controlli che ci vengono messi a disposizione dall’Extended WPF Toolkit community. Abbiamo messo in binding il valore di ClockFontSize con la property Value del controllo, e abbiamo indicato che i bottoncini di spin, alzano o abbassano il valore della variabile di 0.2.

            <TextBlock
                Grid.Row="5"
                Grid.Column="0"
                Margin="4,2,4,2"
                HorizontalAlignment="Right"
                VerticalAlignment="Center"
                Text="Clock Font Family"/>
            <ComboBox 
                Grid.Row="5"
                Grid.Column="1"
                Margin="4,2,4,2"
                HorizontalAlignment="Stretch"
                VerticalAlignment="Center"
                MinHeight="32"
                FontSize="14"
                ItemsSource="{x:Static Fonts.SystemFontFamilies}"
                SelectedItem="{Binding SelectedClock.ClockFontFamily, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
                <ComboBox.ItemTemplate>
                    <DataTemplate>
                        <TextBlock Text="{Binding}" FontFamily="{Binding}"/>
                    </DataTemplate>
                </ComboBox.ItemTemplate>
            </ComboBox>

Nella riga successiva, aggiungiamo la combobox per la selezione della font. WPF ci fornisce una collection di sistema che lista le font installate sulla macchina, la usiamo come datasource del controllo, inoltre, per fare in modo che la lista mostri il nome della font utilizzando la font stessa, semplicemente colleghiamo la FontFamily della Textblock che ci fa da visualizzatore nella combobox al contenuto della lista (ecco perché il semplice Binding senza ulteriori diciture.

clock_font_and_size_03

Il risultato che otterremo è la lista delle font nei caratteri stessi delle font che vediamo qui sopra.

Terminate le modifiche, il nostro orologio non solo sarà colorato a nostro piacimento, ma avrà anche l’aspetto e la dimensione che preferiamo.

Riepilogo

In questa nuova puntata dedicata allo sviluppo di nuove funzionalità sul nostro orologio abbiamo visto le seguenti cose:

  • Come creare una property per ospitare una Font Size, ovvero un double.
  • Come creare una serie di property pilotate il cui valore dipende da quello della property precedente.
  • Come creare una property per ospitare una Font Family.
  • Come effettuare il binding di FontSize e FontFamily per i nostri controlli alle property definite in precedenza.
  • Come utilizzare un controllo DoubleUpDown
  • Come utilizzare un controllo Combobox per visualizzare e selezionare una font.

Potete scaricare il progetto esempio dal link qui indicato:

Se volete invece installare l’orologio, questo è il link del setup

Per installare l’orologio scaricare lo zip, unzipparlo, contiene un file di setup al suo interno ed eseguirlo sul vostro computer.

Per qualsiasi domanda, osservazione, commento, approfondimento, o per segnalare un errore, potete usare il link alla form di contatto in cima alla pagina.