Press "Enter" to skip to content

9 – MultiClock – Aggiungere l’orologio analogico a quello digitale

Aggiungiamo al nostro multi orologio digitale la possibilità di visualizzare anche l’orologio analogico accanto a quello digitale, per farlo, considerato il fatto che questo è un progetto didattico, copieremo il progetto AnalogClock che abbiamo realizzato negli articoli precedenti e lo aggiungeremo alla soluzione MultiClock, nel mondo reale, avendo creato un componente riutilizzabile, terremo l’orologio analogico su una soluzione separata e semplicemente lo pubblicheremmo su una cartella riservata alle librerie di uso comune e faremmo un reference all’orologio analogico nel progetto MultiClock. Per non complicarci troppo la vita invece, facciamo una copia del progetto.

Dopo aver copiato il progetto sulla cartella della soluzione Multiclock.

analog_clock_copy[10]

Andiamo ad aggiungerlo alla soluzione Multiclock.

analog_clock_add_to_solution[9]

Per farlo, ci posizioniamo sulla soluzione nel solution explorer e sul menu contestuale utilizziamo Add e poi Existing Project. Nella cartella che si aprirà per noi, andremo a posizionarci sulla cartella AnalogClock che abbiamo copiato e selezioneremo il progetto AnalogClock.csproj. Una volta fatto, la nostra soluzione diverrà simile a questa immagine:

analog_clock_added[9]

Adesso Analog Clock è parte della nostra soluzione e quindi lo andremo a referenziare in MultiClock per poterlo utilizzare.

analogClock_addreference[9]

Ci posizioniamo su References, e sul menu contestuale selezioniamo Add Reference…

analogClock_addreference_02[9]

Sulla finestra di selezione, selezioniamo Projects e comparirà solo AnalogClock che selezioneremo premendo OK. A questo punto, lo User Control del nostro orologio analogico è a nostra disposizione.

Modifiche a ClockControl.xaml

Iniziamo a vedere come modificare per prima cosa lo user control per aggiungere l’orologio analogico a quello digitale.

 xmlns:gctl="clr-namespace:Dnw.AnalogClock.Controls;assembly=Dnw.AnalogClock"

Dopo aver referenziato la libreria contenente il nostro orologio analogico, dobbiamo renderlo disponibile allo XAML del controllo orologio digitale, in modo che possiamo aggiungerlo e visualizzarlo accanto all’orologio digitale stesso. Per farlo aggiungiamo un namespace che “importa” il contenuto della cartella Controls nella libreria AnalogClock.

<Grid>
 
    <Grid.ColumnDefinitions>
	<ColumnDefinition Width="60*"/>
	<ColumnDefinition Width="40*"/>
    </Grid.ColumnDefinitions>
	<Grid
    		Grid.Column="0">

... here is the previous control grid containing the digital clock components let's see how we changed them...

</Grid>

Il codice xaml qui sopra rappresenta la nuova Grid contenitore che ha una riga con due colonne, la colonna di sinistra conterrà l’orologio digitale, la colonna di destra l’orologio analogico.

<Grid
    Grid.Column="0">

... Here are the digital clock components that don't need any modification.

</Grid>

La grid che contiene i componenti dell’orologio digitale, necessita di una singola modifica, ovvero indicare alla nuova grid contenitore che occupa la sua colonna 0 la prima a sinistra.

<gctl:AClockControl 
    Grid.Column="1"
    Visibility="{Binding AnalogClockIsVisible, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
    Name="AnalogClock"
    />

Sotto alla grid che contiene i controlli dell’orologio digitale, andiamo a inserire il controllo relativo all’orologio analogico. Come potete notare nello xaml, lo abbiamo posizionato sulla colonna 1 inoltre abbiamo collegato la sua visibilità ad una property del model del nostro controllo che si chiama AnalogClockIsVisible e sarà collegata alla property ShowAnalogClock che aggiungeremo alla classe ClockInfo per permettere all’utente di non visualizzare l’orologio analogico se preferisce.

Modifiche a ClockInfo.cs

Iniziamo ora a vedere come abbiamo modificato il codice che pilota gli orologi per integrare l’orologio analogico. La prima modifica, riguarda la classe ClockInfo, che definisce ogni orologio del nostro MultiClock in cui dobbiamo inserire le nuove property necessarie a gestire le informazioni specifiche di configurazione dell’orologio analogico.

public double AnalogClockFontSize
{
    get
    {
        double fs = mClockFontSize * 0.20;
        if (fs < 10.0)
            fs = 10.0;
        return fs;
    }
}

La prima property che aggiungiamo, definisce la dimensione dei caratteri che disegnano le ore sull’orologio analogico, questa, come tutte le altre sarà derivata proporzionalmente a partire dalla dimensione dell’orologio digitale. potete giocare sul moltiplicatore per farle più grandi o più piccole.

public Color HourHandColor
{
    get
    {
        return mHourHandColor;
    }
    set
    {
        mHourHandColor = value;
        OnPropertyChanged(FLD_HourHandColor);
    }
}

La property che definisce il colore della lancetta delle ore, ho omesso e da ora in poi ometterò le dichiarazioni della variabile a livello di classe e della costante con il nome della variabile per la generazione dell’evento property changed sul codice relativo alle property perché ormai dovreste essere familiari con la loro definizione.

public Color MinuteHandColor
{
    get
    {
        return mMinuteHandColor;
    }
    set
    {
        mMinuteHandColor = value;
        OnPropertyChanged(FLD_MinuteHandColor);
    }
}

La property che definisce il colore della lancetta dei minuti

public ClockNumberTypes NumberType
{
    get
    {
        return mNumberType;
    }
    set
    {
        mNumberType = value;
        OnPropertyChanged(FLD_NumberType);
    }
}

La property che definisce il tipo di numerazione che verrà applicato all’orologio.

public Color SecondHandColor
{
    get
    {
        return mSecondHandColor;
    }
    set
    {
        mSecondHandColor = value;
        OnPropertyChanged(FLD_SecondHandColor);
    }
}

Il colore della lancetta dei secondi.

public bool ShowAnalogClock
{
    get
    {
        return mShowAnalogClock;
    }
    set
    {
        mShowAnalogClock = value;
        OnPropertyChanged(FLD_ShowAnalogClock);
    }
}

Il flag che indicherà se l’utente vuole o meno l’orologio analogico.

public Color TicksColor
{
    get
    {
        return mTicksColor;
    }
    set
    {
        mTicksColor = value;
        OnPropertyChanged(FLD_TicksColor);
    }
}

Il colore dei trattini che disegnano ore e minuti, è l’ultima delle property necessarie a gestire l’orologio analogico.

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);
        }
        if (comparator != 0) return comparator;
        comparator = this.NumberType.CompareTo(val.NumberType);
        if (comparator != 0) return comparator;
        comparator = CompareStrings(this.TicksColor.ToString(), val.TicksColor.ToString());
        if (comparator != 0) return comparator;
        comparator = CompareStrings(this.HourHandColor.ToString(), val.HourHandColor.ToString());
        if (comparator != 0) return comparator;
        comparator = CompareStrings(this.MinuteHandColor.ToString(), val.MinuteHandColor.ToString());
        if (comparator != 0) return comparator;
        comparator = CompareStrings(this.SecondHandColor.ToString(), val.SecondHandColor.ToString());
        if (comparator != 0) return comparator;
        comparator = this.ShowAnalogClock.CompareTo(val.ShowAnalogClock);
        return (comparator);
    }
 
    return (comparator);
}

Modifichiamo la funzione CompareTo, per segnalare la modifica a tutte le nuove property, in modo che l’attivazione dei comandi sulla finestra di configurazione degli orologi funzioni correttamente.

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;
    item.HourHandColor = this.HourHandColor;
    item.MinuteHandColor = this.MinuteHandColor;
    item.SecondHandColor = this.SecondHandColor;
    item.ShowAnalogClock = this.ShowAnalogClock;
    item.TicksColor = this.TicksColor;
    item.NumberType = this.NumberType;
}

Modifichiamo la funzione di copia, usata anche dalla Clone per copiare correttamente tutti i nuovi campi del nostro ClockInfo.

public ClockInfo()
{
    TimeZoneName = TimeZoneInfo.Utc.StandardName;
    BackgroundColor = (Color)ColorConverter.ConvertFromString("#FF000000");
    ForegroundColor = (Color)ColorConverter.ConvertFromString("#FFD2DF33");
    HourHandColor = (Color)ColorConverter.ConvertFromString("#FFFFFFFF");
    MinuteHandColor = (Color)ColorConverter.ConvertFromString("#FFABD0BC");
    SecondHandColor = (Color)ColorConverter.ConvertFromString("#FF6AAC88");
    TicksColor = (Color)ColorConverter.ConvertFromString("#FFD2DF33");
    NumberType = ClockNumberTypes.Arab12Numbers;
    ClockFontSize = 64;
    ClockFontFamily = new FontFamily("Courier New");
}

Modifichiamo il costruttore assegnando dei valori di default ai colori e alla forma del nostro orologio analogico, in modo che abbia una forma visibile da subito.

Modifiche a SetClocksWindow.xaml

Per permetterci di predisporre l’interfaccia di generazione degli orologi in modo tale da poter inserire anche la configurazione dell’orologio analogico, dobbiamo aggiungere una serie di controlli nel dettaglio di modifica dell’orologio, inoltre, per rendere più professionale ed utile la lista degli orologi generati, ho inserito una ListView al posto di una ListBox, in modo da ottenere una visualizzazione tabellare.

Vediamo le modifiche, e cosa abbiamo introdotto per effettuarle.

xmlns:lconv="clr-namespace:DNW.MultiClock.Converters"

La prima modifica, è l’introduzione di un nuovo namespace, ovvero, nel progetto, abbiamo creato una nuova cartella Converters, tale cartella ospiterà tutte le classi di tipo converter, cosa sono? Per chi si sta avvicinando a WPF, e non ha avuto ancora occasione di usarli, i Converter sono un tipo di classe che permette di fare piccole “magie”, oggi ne vedremo uno molto semplice, che converte un Color in un SolidColorBrush, così da permetterci di visualizzare le colonne contenenti i colori del colore selezionato, mostrato sullo sfondo selezionato. Ma la classe di tipo converter, è una classe davvero eclettica, che ad esempio ci permette di convertire un valore in un immagine, oppure una stringa in una stringa diversa (ad esempio un codice in una descrizione) e così via. Probabilmente ne dovremo fare degli altri nelle successive implementazioni della nostra applicazione. Per ora, ne creiamo uno facile, la cui implementazione vedremo fra poco.

<Window.Resources>
        <ResourceDictionary>
            <lconv:ColorToSolidBrushConverter x:Key="colorToSolidBrushConverter" />
        </ResourceDictionary>
</Window.Resources>

Inseriamo nella finestra un nuovo tag, il tag Window.Resources, tale Tag ospita al suo interno un ResourceDictionary, in cui definiamo come accedere al nuovo converter. La zona Resources, ha svariati usi in un file XAML, il primo è mappare risorse disponibili ai controlli, un altro è quello di permettere di definire stili da applicare a più controlli una volta sola. Un altro ancora è mappare risorse in lingua ma ve ne sono molti altri.

Modifichiamo ora il tag Grid che contiene i controlli con cui potevamo modificare l’aspetto e la funzione di ogni orologio. La modifica è abbastanza semplice:

<Grid.ColumnDefinitions>
    <ColumnDefinition Width="Auto"/>
    <ColumnDefinition Width="50*"/>
    <ColumnDefinition Width="Auto"/>
    <ColumnDefinition Width="50*"/>
</Grid.ColumnDefinitions>

Quello che facciamo, è aggiungere 2 nuove colonne sulla destra della grid, una per le descrizioni ed una per i controlli che definiscono la parametrizzazione dell’orologio analogico.

All’interno della Grid, adesso andiamo ad aggiungere i controlli per modificare le nuove property aggiunte alla classe ClockInfo:

<TextBlock
	Grid.Row="0"
	Grid.Column="3"
	Grid.ColumnSpan="2"
	Margin="4,2,4,2"
	HorizontalAlignment="Left"
	VerticalAlignment="Center"
	FontWeight="Bold"
	Text="Analog Clock Configuration Parameters"/>

Prima un titolo, per indicare che sono i parametri dell’orologio analogico.

<CheckBox
	Grid.Row="1"
	Grid.Column="4"
	Margin="4,2,4,2"
	VerticalAlignment="Center"
	HorizontalAlignment="Left"
	IsChecked="{Binding SelectedClock.ShowAnalogClock, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
	>
<TextBlock
	Margin="4,2,4,2"
	HorizontalAlignment="Right"
	VerticalAlignment="Center"
	Text="Show the Analog Clock"/>
			</CheckBox>

La checkbox che ci permetterà di decidere se visualizzare o meno l’orologio analogico accanto a quello digitale.

<TextBlock
	Grid.Row="2"
	Grid.Column="2"
	Margin="4,2,4,2"
	HorizontalAlignment="Right"
	VerticalAlignment="Center"
	Text="Ticks Color"/>
<wpfx:ColorPicker 
	Grid.Row="2"
	Grid.Column="3"
	VerticalAlignment="Center"
	HorizontalAlignment="Stretch"
	Height="30" DisplayColorAndName="True" 
	SelectedColor="{Binding SelectedClock.TicksColor, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
	ShowAdvancedButton="True"
	Margin="4,2,4,2" />

Il testo e il color picker per il colore da dare ai Ticks delle ore e minuti sull’orologio analogico.

<TextBlock
    Grid.Row="3"
    Grid.Column="2"
    Margin="4,2,4,2"
    HorizontalAlignment="Right"
    VerticalAlignment="Center"
    Text="Hours Numbering Type"/>
 
<ComboBox
    Grid.Row="3"  
    Grid.Column="3"
    Margin="4,2,4,2"
    HorizontalAlignment="Stretch"
    VerticalAlignment="Center"
    SelectedItem="{Binding SelectedClock.NumberType, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"				
    ItemsSource="{Binding NumberingTypes}"/>

Il testo e la combobox per selezionare come numerare le ore sull’orologio analogico.

<TextBlock
    Grid.Row="4"
    Grid.Column="2"
    Margin="4,2,4,2"
    HorizontalAlignment="Right"
    VerticalAlignment="Center"
    Text="Hours Hand Color"/>
            <wpfx:ColorPicker 
    Grid.Row="4"
    Grid.Column="3"
    VerticalAlignment="Center"
    HorizontalAlignment="Stretch"
    Height="30" DisplayColorAndName="True" 
    SelectedColor="{Binding SelectedClock.HourHandColor, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
    ShowAdvancedButton="True"
    Margin="4,2,4,2" />
            <TextBlock
    Grid.Row="5"
    Grid.Column="2"
    Margin="4,2,4,2"
    HorizontalAlignment="Right"
    VerticalAlignment="Center"
    Text="Minutes Hand Color"/>
            <wpfx:ColorPicker 
    Grid.Row="5"
    Grid.Column="3"
    VerticalAlignment="Center"
    HorizontalAlignment="Stretch"
    Height="30" DisplayColorAndName="True" 
    SelectedColor="{Binding SelectedClock.MinuteHandColor, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
    ShowAdvancedButton="True"
    Margin="4,2,4,2" />
            <TextBlock
    Grid.Row="6"
    Grid.Column="2"
    Margin="4,2,4,2"
    HorizontalAlignment="Right"
    VerticalAlignment="Center"
    Text="Seconds Hand Color"/>
            <wpfx:ColorPicker 
    Grid.Row="6"
    Grid.Column="3"
    VerticalAlignment="Center"
    HorizontalAlignment="Stretch"
    Height="30" DisplayColorAndName="True" 
    SelectedColor="{Binding SelectedClock.SecondHandColor, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
    ShowAdvancedButton="True"
    Margin="4,2,4,2" />

Testo e color picker per i 3 valori relativi al colore delle 3 lancette dell’orologio.

E con questo, il dettaglio è fatto, adesso vediamo come è fatto il nuovo controllo ListView che prende il posto della precedente ListBox in modo da darci una lista più dettagliata e comprensibile, oltre che usabile.

<ListView 
    Grid.Row="2"
    ItemsSource="{Binding Clocks, UpdateSourceTrigger=PropertyChanged}"
    SelectedItem="{Binding SelectedClock, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
    SelectionMode="Single" Margin="0" 
    IsSynchronizedWithCurrentItem="True"   >

... Here will be the list view columns definition

</ListView>

Per creare la listview è sufficiente questo tag, ove indichiamo la posizione nella grid principale, la lista che fornisce gli oggetti da visualizzare, il Binding al SelectedItem in modo da far funzionare correttamente il dettaglio, il metodo di selezione dati, ovvero la singola riga come era per la ListBox.

<ListView.View>
    <GridView>

... Here the real columns definition

    </GridView>
</ListView.View>

Per definire il layout della lista, quindi come è fatta, si utilizza la definizione della View della nostra ListView, in cui viene inserita una GridView.

All’interno di questi tag, per ogni colonna verrà generato uno dei seguenti Tag:

<GridViewColumn Header="Primary" Width="50">
	<GridViewColumn.CellTemplate>
        	<DataTemplate>
			<CheckBox IsEnabled="False" Margin="-4,0,-4,0" IsChecked="{Binding IsPrimaryClock, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"  />
		</DataTemplate>
	</GridViewColumn.CellTemplate>
</GridViewColumn>

Se la colonna ha bisogno di un controllo diverso da un semplice testo per essere visualizzata, è necessario creare una GridViewColumn con un CellTemplate, in questo template possiamo inserire qualsiasi controllo vogliamo e connetterlo in binding con uno dei campi dell’oggetto che fa da sorgente dati, in questo caso il nostro ClockInfo. Come vedete, abbiamo usatola property IsPrimaryClock.

<GridViewColumn 
    Header="Order" 
    DisplayMemberBinding="{Binding Order, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" 
    Width="40"/>

Il secondo, più semplice tipo di colonna è come indicato, quello che può essere rappresentato con una stringa (anche se un numero come in questo caso) se non necessario il CellTemplate, basta la definizione del DisplayMemberBinding per descrivere la property visualizzata. Name, TimeZone e FontSize hanno lo stesso tipo di definizione per la colonna.

<GridViewColumn 
	Header="Clock Colors" 
	Width="80">
	<GridViewColumn.CellTemplate>
		<DataTemplate>
			<TextBlock 
				Margin="0"
				Padding="2"
				Text="{Binding ForegroundColor, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"  
				Background="{Binding BackgroundColor, Mode=OneWay, UpdateSourceTrigger=PropertyChanged, 
				Converter={StaticResource colorToSolidBrushConverter}}" 
				Foreground="{Binding ForegroundColor, Mode=OneWay, UpdateSourceTrigger=PropertyChanged, 
				Converter={StaticResource colorToSolidBrushConverter}}" 
				/>
		</DataTemplate>
	</GridViewColumn.CellTemplate>
</GridViewColumn>

Quando invece abbiamo bisogno di fare qualcosa di meno semplice, utilizziamo nuovamente il Data Template. In questo caso, vogliamo visualizzare come testo il valore del colore del carattere dell’orologio digitale, ma vogliamo che il carattere sia del colore scelto e lo sfondo su cui è visualizzato sia del colore dello sfondo scelto per l’orologio, in modo che l’utente possa giudicare come i colori si abbinano prima di aggiornare i dati degli orologi. Come potete vedere, per ottenere tutto ciò, abbiamo usato le property BackgroundColor e ForegroundColor, per definire il valore delle property Background e Foreground della TextBlock. Ma, queste due property sono di tipo Brush, mentre le nostre property sono di tipo Color, ecco perché utilizziamo il converter a cui abbiamo accennato ed il cui codice vedremo fra poco, per fornire un SolidColorBrush al posto di un Color.

<GridViewColumn 
	Header="Font Family" 
	Width="200">
	<GridViewColumn.CellTemplate>
		<DataTemplate>
			<TextBlock 
				Margin="0"
				Padding="2"
				FontFamily="{Binding ClockFontFamily, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" 
				FontSize="{Binding NameFontSize, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" 
				Text="{Binding ClockFontFamily, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"  
				Background="{Binding BackgroundColor, Mode=OneWay, UpdateSourceTrigger=PropertyChanged, 
				Converter={StaticResource colorToSolidBrushConverter}}" 
				Foreground="{Binding ForegroundColor, Mode=OneWay, UpdateSourceTrigger=PropertyChanged, 
				Converter={StaticResource colorToSolidBrushConverter}}" 
				/>
		</DataTemplate>
	</GridViewColumn.CellTemplate>
</GridViewColumn>

Per la colonna Font Family facciamo qualcosa di più articolato ancora, infatti, vogliamo visualizzare il nome della font, nella font stessa, con i colori dell’orologio, e per evitare valori enormi, useremo la dimensione che sarà usata per il Nome dell’orologio digitale.

Non c’è nulla di complicato, semplicemente, colleghiamo la property del controllo FontFamily alla property di ClockInfo ClockFontFamily, la FontSize, a NameFontSize, Background a BackfroundColor (usando il converter) e Foreground a ForegroundColor (usando il converter).

Replichiamo per le colonne mancanti (il codice completo è nel progetto esempio.)

Modifiche a SetClocksWindow.xaml.cs

Le modifiche al codice dietro alla finestra sono abbastanza semplici:

public ObservableCollection<ClockNumberTypes> NumberingTypes
{
    get
    {
        return mNumberingTypes;
    }
    set
    {
        mNumberingTypes = value;
        OnPropertyChanged(FLD_NumberingTypes);
    }
}

Aggiungiamo una property che conterrà la lista dei possibili valori della property NumberType. E la inizializziamo nel costruttore della classe, se ce ne dimenticassimo, avremo un immediato errore.

private void SetClocksWindow_Loaded(object sender, RoutedEventArgs e)
{
    try
    {
        var timeZones = TimeZoneInfo.GetSystemTimeZones();
        foreach (TimeZoneInfo tzi in timeZones)
        {
            TimeZones.Add(tzi);
        }
 
        var clocknumbers = Enum.GetValues(typeof(ClockNumberTypes));
        foreach (ClockNumberTypes cnt in clocknumbers)
        {
            NumberingTypes.Add(cnt);
        }
 
        Clocks = AppContext.Instance.ConfigData.Clocks.Clone();
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message, "Error (4)", MessageBoxButton.OK, MessageBoxImage.Error);
    }
}

Modifichiamo il metodo Loaded per riempire la lista dei valori dei tipi di numerazione dell’orologio analogico. Ed è tutto fatto. Le modifiche che avevamo apportato a ClockInfo, si occuperanno del resto.

multiclock_w_analog_02[8]

Questo è un esempio del nostro nuovo orologio con digitale ed analogico.

multiclock_w_analog_03[8]

Questa è la nuova versione della finestra per configurare gli orologi.

Non ci resta che vedere come è fatto il converter che permette di trasformare il semplice testo in testo colorato nella nostra lista degli orologi:

La classe ColorToSolidBrushConverter.cs

using System;
using System.Windows.Data;
using System.Globalization;
using System.Windows.Media;
 
namespace DNW.MultiClock.Converters
{
    [ValueConversion(typeof(Color), typeof(SolidColorBrush))]
    public class ColorToSolidBrushConverter : IValueConverter
    {
 
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            SolidColorBrush result = new SolidColorBrush(Colors.Transparent);
            if (value != null)
            {
                Color input = (Color)value;
                result = new SolidColorBrush(input);
            }
            return result;
        }
 
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            //If you don't need a convert back this does nothing
            return value;
        }
 
    }
}

La classe è molto semplice, il fatto che implementi IValueConverter indica alla User Interface come usarla, L’attributo ValueConversion, fa in modo che se usata con un tipo di dato sbagliato venga sollevata una eccezione. Il converter, ha 2 metodi, Convert e ConvertBack, per i converte come il nostro, che hanno un solo “verso” per così dire, ovvero sono usati solo per il display, non implementiamo nulla nel metodo ConvertBack, invece, se volessimo creare un converter per convertire un valore su un controllo di edit, come ad esempio una combobox, o una textbox, dobbiamo implementare entrambe le conversioni.

Riepilogo

Che cosa abbiamo spiegato in questo articolo:

  • Come utilizzare una libreria di controlli in un progetto eseguibile, quindi come referenziarla e come usare gli oggetti in essa contenuti.
  • Come referenziare un namespace in una diversa libreria all’interno di XAML
  • Come definire l’area Resources di una window, e come includere nelle resources la definizione di un Converter.
  • Come modificare una grid per aggiungere nuove colonne e nuovi controlli.
  • Come sostituire una ListBox con una ListView
  • Come creare colonne specializzate e formattate in una ListView
  • Come effettuare il Binding alle Property degli elementi di una lista, sulle property dei controlli inseriti nella definizione delle colonne, in modo da non pilotare la semplice visualizzazione del valore, ma usare il valore per modificare l’aspetto del controllo.
  • Come creare un Converter, per trasformare un Color in un Brush
  • Come usare un Converter per fornire il valore ad una delle property di un controllo.
  • Come utilizzare un controllo definito su una diversa libreria all’interno di uno user control
  • Come visualizzare un orologio analogico accanto a quello digitale.

Potete scaricare il progetto esempio dal link qui indicato:

Potete scaricare l’applicazione funzionante dal link qui indicato:

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