Continuando con los controles de usuario en WPF, voy a mostrarles un control un poco más complejo y en este caso se trata de un reloj un poco especial, se trata de un reloj binario.
Se trata de un reloj, pero los dígitos se muestran uno a uno en modo binario mediante una bola de indicador. Por ejemplo para indicar la hora 15, se muestra un 1 en binario 001 y un 5 en binario 101.He creado un control básico que hará las veces de indicador con un tono grisáceo cuando el indicador está apagado o es un cero y un tono verdoso para indicar un 1 o cuando está encendido.

Y ahora muestro el código de la bolita
<UserControl x:Class="Controls.EllipseUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:Controls="clr-namespace:Controls"
mc:Ignorable="d"
d:DesignHeight="48" d:DesignWidth="48" x:Name="ellipseUserControl">
<Grid x:Name="gridEllipse" Height="{Binding ElementName=ellipseUserControl,Path=Height}"
Width="{Binding ElementName=ellipseUserControl,Path=Width}">
<Grid.Resources>
<Style x:Key="ellipseStyle" TargetType="Ellipse">
<Setter Property="VerticalAlignment" Value="Stretch"/>
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="Margin" Value="2"/>
<Setter Property="Fill">
<Setter.Value>
<RadialGradientBrush GradientOrigin="0.6,0.2"
Center="0.6,0.2"
RadiusX="0.4" RadiusY="0.4">
<RadialGradientBrush.GradientStops>
<GradientStop Color="White" Offset="0" />
<GradientStop Color="{Binding ElementName=gridEllipse, Path=Parent.ActiveColor}" Offset="1.5" />
</RadialGradientBrush.GradientStops>
</RadialGradientBrush>
</Setter.Value>
</Setter>
</Style>
</Grid.Resources>
<Ellipse x:Name="ellipse" Style="{StaticResource ellipseStyle}"/>
</Grid>
</UserControl>
Podemos apreciar que el control básico solo tiene un Grid y dentro de este una ellipse, la ellipse tiene un estilo declarado en Grid.Resources y el cual tiene un degradado radial y desde una de sus propiedades GradientStop enlazamos con una propiedad DependecyProperty ActiveColor, de modo que podamos convertir valores booleanos en colores y se la asignemos a esta propiedad y por tanto esta propiedad cambiará el color de GradientStop. Esta propiedad la declaramos en la clase del control de usuario del siguiente modo:
public Color ActiveColor
{
get { return (Color)GetValue(ActiveColorProperty); }
set { SetValue(ActiveColorProperty, value); }
}
// Using a DependencyProperty as the backing store for IsActiveColor. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ActiveColorProperty =
DependencyProperty.Register("ActiveColor", typeof(Color), typeof(EllipseUserControl), new PropertyMetadata(Brushes.Red.Color));
Ahora vamos al trabajo duro, que es crear un control de usuario más complejo, basado en unos cuantos controles básicos. Para las horas usaremos para:
- Horas. Primer dígito 2, o vale 0, 1 o 2 (00, 01 o 11). Segundo dígito 4 indicadores, desde 0 a 9 y el 9 es 1001 en binario.
- Minutos. Primer dígito. Valor máximo es 5, por tanto 3 indicadores (101), segundo dígito valor máximo 9, cuatro indicadores (1001)
- Segundos. Igual que los minutos.
Ahora que tenemos el diseño del reloj, lo generamos en XAML
<UserControl x:Class="Controls.BinaryClock"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:Controls="clr-namespace:Controls"
mc:Ignorable="d"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
d:DesignHeight="155" d:DesignWidth="180" >
<UserControl.Resources>
<Controls:BooleanToBrushes x:Key="btb"/>
</UserControl.Resources>
<Grid x:Name="grid">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="30"/>
<ColumnDefinition Width="30"/>
<ColumnDefinition Width="30"/>
<ColumnDefinition Width="30"/>
<ColumnDefinition Width="30"/>
<ColumnDefinition Width="30"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="25"/>
<RowDefinition Height="25"/>
<RowDefinition Height="25"/>
<RowDefinition Height="25"/>
<RowDefinition Height="25"/>
<RowDefinition Height="25"/>
<RowDefinition Height="25"/>
</Grid.RowDefinitions>
<!-- HORAS -->
<TextBlock Text="H" Grid.Column="0" Grid.Row="0" Grid.ColumnSpan="2" HorizontalAlignment="Center" FontSize="20"/>
<Border BorderThickness="1" BorderBrush="LightCoral" Grid.Column="0" Grid.Row="1" Grid.ColumnSpan="2" Grid.RowSpan="4" Margin="1"/>
<Controls:EllipseUserControl x:Name="hour11" Grid.Column="0" Grid.Row="4" ActiveColor="{Binding ElementName=grid, Path=Parent.H10, Converter={StaticResource btb}}"/>
<Controls:EllipseUserControl x:Name="hour12" Grid.Column="0" Grid.Row="3" ActiveColor="{Binding ElementName=grid, Path=Parent.H11, Converter={StaticResource btb}}"/>
<Controls:EllipseUserControl x:Name="hour21" Grid.Column="1" Grid.Row="4" ActiveColor="{Binding ElementName=grid, Path=Parent.H20, Converter={StaticResource btb}}"/>
<Controls:EllipseUserControl x:Name="hour22" Grid.Column="1" Grid.Row="3" ActiveColor="{Binding ElementName=grid, Path=Parent.H21, Converter={StaticResource btb}}"/>
<Controls:EllipseUserControl x:Name="hour24" Grid.Column="1" Grid.Row="2" ActiveColor="{Binding ElementName=grid, Path=Parent.H22, Converter={StaticResource btb}}"/>
<Controls:EllipseUserControl x:Name="hour28" Grid.Column="1" Grid.Row="1" ActiveColor="{Binding ElementName=grid, Path=Parent.H23, Converter={StaticResource btb}}"/>
<!-- MINUTOS -->
<TextBlock Text="M" Grid.Column="2" Grid.Row="0" Grid.ColumnSpan="2" HorizontalAlignment="Center" FontSize="20"/>
<Border BorderThickness="1" BorderBrush="LightBlue" Grid.Column="2" Grid.Row="1" Grid.ColumnSpan="2" Grid.RowSpan="4" Margin="1"/>
<Controls:EllipseUserControl x:Name="minute11" Grid.Column="2" Grid.Row="4" ActiveColor="{Binding ElementName=grid, Path=Parent.M10, Converter={StaticResource btb}}"/>
<Controls:EllipseUserControl x:Name="minute12" Grid.Column="2" Grid.Row="3" ActiveColor="{Binding ElementName=grid, Path=Parent.M11, Converter={StaticResource btb}}"/>
<Controls:EllipseUserControl x:Name="minute14" Grid.Column="2" Grid.Row="2" ActiveColor="{Binding ElementName=grid, Path=Parent.M12, Converter={StaticResource btb}}"/>
<Controls:EllipseUserControl x:Name="minute21" Grid.Column="3" Grid.Row="4" ActiveColor="{Binding ElementName=grid, Path=Parent.M20, Converter={StaticResource btb}}"/>
<Controls:EllipseUserControl x:Name="minute22" Grid.Column="3" Grid.Row="3" ActiveColor="{Binding ElementName=grid, Path=Parent.M21, Converter={StaticResource btb}}"/>
<Controls:EllipseUserControl x:Name="minute24" Grid.Column="3" Grid.Row="2" ActiveColor="{Binding ElementName=grid, Path=Parent.M22, Converter={StaticResource btb}}"/>
<Controls:EllipseUserControl x:Name="minute28" Grid.Column="3" Grid.Row="1" ActiveColor="{Binding ElementName=grid, Path=Parent.M23, Converter={StaticResource btb}}"/>
<!-- SEGUNDOS -->
<TextBlock Text="S" Grid.Column="4" Grid.Row="0" Grid.ColumnSpan="2" HorizontalAlignment="Center" FontSize="20"/>
<Border BorderThickness="1" BorderBrush="LightGreen" Grid.Column="4" Grid.Row="1" Grid.ColumnSpan="2" Grid.RowSpan="4" Margin="1"/>
<Controls:EllipseUserControl x:Name="second11" Grid.Column="4" Grid.Row="4" ActiveColor="{Binding ElementName=grid, Path=Parent.S10, Converter={StaticResource btb}}"/>
<Controls:EllipseUserControl x:Name="second12" Grid.Column="4" Grid.Row="3" ActiveColor="{Binding ElementName=grid, Path=Parent.S11, Converter={StaticResource btb}}"/>
<Controls:EllipseUserControl x:Name="second14" Grid.Column="4" Grid.Row="2" ActiveColor="{Binding ElementName=grid, Path=Parent.S12, Converter={StaticResource btb}}"/>
<Controls:EllipseUserControl x:Name="second21" Grid.Column="5" Grid.Row="4" ActiveColor="{Binding ElementName=grid, Path=Parent.S20, Converter={StaticResource btb}}"/>
<Controls:EllipseUserControl x:Name="second22" Grid.Column="5" Grid.Row="3" ActiveColor="{Binding ElementName=grid, Path=Parent.S21, Converter={StaticResource btb}}"/>
<Controls:EllipseUserControl x:Name="second24" Grid.Column="5" Grid.Row="2" ActiveColor="{Binding ElementName=grid, Path=Parent.S22, Converter={StaticResource btb}}"/>
<Controls:EllipseUserControl x:Name="second28" Grid.Column="5" Grid.Row="1" ActiveColor="{Binding ElementName=grid, Path=Parent.S23, Converter={StaticResource btb}}"/>
<TextBlock Text="{Binding ElementName=grid, Path=Parent.Hour}" Grid.Column="0" Grid.Row="5" Grid.ColumnSpan="2" HorizontalAlignment="Center" FontSize="22"/>
<TextBlock Text="{Binding ElementName=grid, Path=Parent.Minutes}" Grid.Column="2" Grid.Row="5" Grid.ColumnSpan="2" HorizontalAlignment="Center" FontSize="22"/>
<TextBlock Text="{Binding ElementName=grid, Path=Parent.Seconds}" Grid.Column="4" Grid.Row="5" Grid.ColumnSpan="2" HorizontalAlignment="Center" FontSize="22"/>
</Grid>
</UserControl>
como podéis apreciar, he distribuido los controles para dar forma al reloj quedando su diseño así

Cada indicador usa una clase converter para convertir el valor booleano en un color y con esto finaliza el código XAML.
public class BooleanToBrushes : IValueConverter
{
#region Constructor
#endregion
#region Propiedades
#endregion
#region IValueConverter
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
bool bValue = (bool)value;
if (bValue)
{
return Brushes.Green.Color;
}
else
{
return Brushes.Gray.Color;
}
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return null;
}
#endregion
}
Vamos al código. Hay una propiedad booleana por cada uno de los indicadores. Un DispatcherTime para calcular los valores en cada segundo y un par de métodos para convertir la hora a binario y pasarla a cada uno de los indicadores mediante las propiedades. Paso el código.
public partial class BinaryClock : UserControl,INotifyPropertyChanged
{
public BinaryClock()
{
InitializeComponent();
dispatcher.Tick += Dispatcher_Tick; ;
dispatcher.Interval = TimeSpan.FromSeconds(1);
dispatcher.Start();
}
#region Privates Method
List setDecimalToBinary(int value, bool isHour)
{
List values = new List();
string valueToString = value.ToString("00");
string digit1 = valueToString.Substring(0, 1);
string digit2 = valueToString.Substring(1, 1);
string binaryDigit1 = convertToBinary(Convert.ToInt16(digit1)).PadLeft(2, '0');
if (!isHour)
{
binaryDigit1 = binaryDigit1.PadLeft(3, '0');
}
string binaryDigit2 = convertToBinary(Convert.ToInt16(digit2)).PadLeft(4, '0');
foreach (var item in binaryDigit1 + binaryDigit2)
{
values.Add(item.Equals('0') ? false : true);
}
return values;
}
string convertToBinary(int value)
{
return Convert.ToString(value, 2);
}
List setDecimalToBinary(int value)
{
return setDecimalToBinary(value, false);
}
private void Dispatcher_Tick(object sender, EventArgs e)
{
DateTime horaActual = DateTime.Now;
Hour = horaActual.Hour.ToString();
Minutes = horaActual.Minute.ToString();
Seconds = horaActual.Second.ToString();
var hora = setDecimalToBinary(horaActual.Hour, true);
H10 = hora[1];
H11 = hora[0];
H20 = hora[5];
H21 = hora[4];
H22 = hora[3];
H23 = hora[2];
var minutos = setDecimalToBinary(horaActual.Minute);
M10 = minutos[2];
M11 = minutos[1];
M12 = minutos[0];
M20 = minutos[6];
M21 = minutos[5];
M22 = minutos[4];
M23 = minutos[3];
var segundos = setDecimalToBinary(horaActual.Second);
S10 = segundos[2];
S11 = segundos[1];
S12 = segundos[0];
S20 = segundos[6];
S21 = segundos[5];
S22 = segundos[4];
S23 = segundos[3];
}
#endregion
public event PropertyChangedEventHandler PropertyChanged;
#region Public Methods
///
<summary>
/// Inicia el evento de cambio de propiedad
/// </summary>
/// Nombre de la propiedad
public void OnPropertyChanged(string p_PropertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(p_PropertyName));
}
}
#endregion
#region Properties & fields
private bool h10;
private bool h11;
private bool h20;
private bool h21;
private bool h22;
private bool h23;
private bool m10;
private bool m11;
private bool m12;
private bool m20;
private bool m21;
private bool m22;
private bool m23;
private bool s10;
private bool s11;
private bool s12;
private bool s20;
private bool s21;
private bool s22;
private bool s23;
DispatcherTimer dispatcher = new DispatcherTimer();
private string hour;
private string minute;
private string seconds;
public string Hour
{
get { return hour; }
set
{
hour = value;
OnPropertyChanged("Hour");
}
}
public string Minutes
{
get { return minute; }
set
{
minute = value;
OnPropertyChanged("Minutes");
}
}
public string Seconds
{
get { return seconds; }
set
{
seconds = value;
OnPropertyChanged("Seconds");
}
}
// HORAS. 1ER DIGITO
public bool H10
{
get { return h10; }
set
{
h10 = value;
OnPropertyChanged("H10");
}
}
public bool H11
{
get { return h11; }
set
{
h11 = value;
OnPropertyChanged("H11");
}
}
// HORAS. 2º DIGITO
public bool H20
{
get { return h20; }
set
{
h20 = value;
OnPropertyChanged("H20");
}
}
public bool H21
{
get { return h21; }
set
{
h21 = value;
OnPropertyChanged("H21");
}
}
public bool H22
{
get { return h22; }
set
{
h22 = value;
OnPropertyChanged("H22");
}
}
public bool H23
{
get { return h23; }
set
{
h23 = value;
OnPropertyChanged("H23");
}
}
//MINUTOS. 1ER DIGITO
public bool M10
{
get { return m10; }
set
{
m10 = value;
OnPropertyChanged("M10");
}
}
public bool M11
{
get { return m11; }
set
{
m11 = value;
OnPropertyChanged("M11");
}
}
public bool M12
{
get { return m12; }
set
{
m12 = value;
OnPropertyChanged("M12");
}
}
// MINUTOS. 2º DIGITO
public bool M20
{
get { return m20; }
set
{
m20 = value;
OnPropertyChanged("M20");
}
}
public bool M21
{
get { return m21; }
set
{
m21 = value;
OnPropertyChanged("M21");
}
}
public bool M22
{
get { return m22; }
set
{
m22 = value;
OnPropertyChanged("M22");
}
}
public bool M23
{
get { return m23; }
set
{
m23 = value;
OnPropertyChanged("M23");
}
}
// SEGUNDOS. 1ER DIGITO
public bool S10
{
get { return s10; }
set
{
s10 = value;
OnPropertyChanged("S10");
}
}
public bool S11
{
get { return s11; }
set
{
s11 = value;
OnPropertyChanged("S11");
}
}
public bool S12
{
get { return s12; }
set
{
s12 = value;
OnPropertyChanged("S12");
}
}
// SEGUNDOS. 2º DIGITO
public bool S20
{
get { return s20; }
set
{
s20 = value;
OnPropertyChanged("S20");
}
}
public bool S21
{
get { return s21; }
set
{
s21 = value;
OnPropertyChanged("S21");
}
}
public bool S22
{
get { return s22; }
set
{
s22 = value;
OnPropertyChanged("S22");
}
}
public bool S23
{
get { return s23; }
set
{
s23 = value;
OnPropertyChanged("S23");
}
}
#endregion
}
Pues con este código, cada segundo, se comprueba la hora y se pasa a cada uno de los indicadores un valor booleano correspondiente a su valor en binario, este se convierte en un color con el converter y et voilá, un reloj binario. Le he añadido la hora con números arábigos para que podáis contrastar. Y el resultado… en la cabecera.Espero que os haya gustado.