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.