Binding WPF vs Binding Android – I

Binding WPF vs Binding Android – I
Este post, es el inicio de una tetralogía con el que pretendo mostrar los aspectos de desarrollo de ambas plataformas, Microsoft y Android en cuanto al uso de la arquitectura MVVM y Databinding.
Antes de nada veamos cuales son las diferencias esenciales del modelo vista controlador (MVC) y el modelo vista vista-modelo (MVVM).
Anuncios

Controles de Usuario en WPF 2

Controles de Usuario en WPF 2
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.

indicadores

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í
bin01
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 &amp; 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.

Tabla periódica 2

Tabla periódica 2
En el post que escribí sobre la tabla periódica, pudimos aprender como usar MVVM (Model View ViewModel) y hacer aplicaciones robustas y consistentes con muy poco código; por una parte teníamos las clases ViewModel y por otra la interfaz gráfica que se encarga de realizar los binding necesarios para visualizar su contenido. En aquel caso, cargábamos los datos de los elementos de la tabla periódica desde un archivo xml y se mostraban en pantalla ordenados tal y como conocemos la tabla periódica actualmente; pulsando sobre cualquier elemento, se visualizaba un elemento con algunos datos ampliados, bueno pues en esta ocasión vamos a imaginar que nuestro cliente nos solicita incluir en nuestra tabla un método que visualice la temperatura y según vaya esta variando, el estado de cada elemento lo haga también, ya sea inferior al punto de fusión, estado sólido, sea superior al punto de fusión e inferior al punto de ebullición, estado líquido, o sea superior a este último en que el estado cambiaría a estado gaseoso. Aquí os muestro un diagrama de cambio de estado y un ejemplo del elemento Galio y los puntos de fusión y ebullición.TP201.png

TP202.png

En el archivo xml ya teníamos incluidos los datos correspondientes a los puntos de fusión y ebullición, por tanto solo nos queda implementar nuestra modificación.

En el anterior post sobre la tabla periódica, el estado del elemento se trataba mediante un converter(clase que implementaba la interfaz IValueConverter en el binding al que le pasábamos el estado y devolvía un color, de modo que según el estado, se le asignaba un color al símbolo. En este caso además del estado, tenemos más parámetros y son la temperatura actual y los puntos de fusión y ebullición. Y ¿cómo hacemos para mostrar los estados de los elementos con variación de temperatura? Se me ocurre incluir un control Sliderque varíe desde los -2500ºC hasta los 6000ºC (valor inferior y superior del menor y mayor valor de fusión y ebullición respectivamente) y el valor de este pasarlo junto a los valores de los puntos de fusión y ebullición de cada elemento, pero debéis recordar que la clase que implementa IValueConverter solo podíamos pasar un valor y un parámetro, por lo que para nuestra modificación no nos serviría. Entonces es cuando tenemos que echar mano de otra interfaz muy similar pero que se adapta mucho más a nuestras necesidades y es IMultiValueConverter. Esta interfaz contiene dos métodos Convert y ConvertBack como la interfaz IValueConverter pero con la diferencia de que los valores que se pasan se trata de un array de objetos, por lo cual ahora podemos pasar todos nuestros valores al conversor; a continuación os muestro la clase.

    public class StateToColorConverter : IMultiValueConverter
    {
        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            SolidColorBrush scb = new SolidColorBrush();
            try
            {
                double temp= double.Parse(values[0].ToString());
                double meltingPoint = (double)values[1];
                double boilingPoint = (double)values[2];
                string state = values[3].ToString();

                //parameter
                if (temp <= boilingPoint)
                {
                    // Sólido
                    if (state == "Artificial") scb.Color = Brushes.DarkRed.Color;
                    else scb.Color = Brushes.Black.Color;
                }
                else if (temp > boilingPoint && temp < meltingPoint)
                {
                    // Líquido
                    scb.Color = Brushes.Blue.Color;
                }
                else
                {
                    // Gas
                    scb.Color = Brushes.LightBlue.Color;
                }
                return scb;

            }
            catch (InvalidCastException)
            {
                return scb;
            }
        }

        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            return null;
        }

    }

El código XAML para enlazar los datos con esta clase, debemos hacerlo con un multibinding. Lo primero, instanciar la clase y asignarle un key , segundo desde la propiedad que queremos obtener el color que es el control que visualiza el símbolo con TextBlock.Foreground, le añadimos el tag y posteriormente añadimos tantos binding como valores queremos pasar

<UserControl x:Class="DimitriMendeleyev.ElementControl"
             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:local="clr-namespace:DimitriMendeleyev"
             mc:Ignorable="d"
             xmlns:Controls="http://metro.mahapps.com/winfx/xaml/controls"
             d:DesignHeight="60" d:DesignWidth="60" x:Name="_element">
    <UserControl.Resources>
        <local:SerialChemicalToColorConverter x:Key="sctcc"/>
        <local:StateToColorConverter x:Key="stcc"/>
    </UserControl.Resources>
    <Grid>
        <Button Width="auto" Height="auto"
                Background="{Binding Path=SerialChemical, Converter={StaticResource sctcc}, Mode=OneTime}"
                x:Name="buttonElement">
            <StackPanel>
                <TextBlock x:Name="AtomicNumberTextBlock" Text="{Binding AtomicNumber, Mode=OneTime}" FontSize="12" TextAlignment="Center"/>
                <TextBlock x:Name="SymbolTextBlock" Text="{Binding Symbol, Mode=TwoWay}" FontSize="15" TextAlignment="Center" FontWeight="Bold">
                    <TextBlock.Foreground>
                        <MultiBinding Converter="{StaticResource stcc}">
                            <Binding Path="Temperature"/>
                            <Binding Path="MeltingPoint"/>
                            <Binding Path="BoilingPoint"/>
                            <Binding Path="StandarState"/>
                        </MultiBinding>
                    </TextBlock.Foreground>
                </TextBlock>
                <TextBlock x:Name="NameTextBlock" Text="{Binding Name, Mode=OneTime}" FontSize="10" TextAlignment="Center" />
            </StackPanel>
        </Button>
    </Grid>
</UserControl>

Queda un detalle y es que la temperatura actual hay que pasarla a cada elemento para que este pueda hacer el binding y esto lo he conseguido añadiendo una nueva propiedad a la clase Element y PeriodicTable, entonces enlazamos la propiedad Value del slider que hemos incluido con la propiedad de la PeriodicTable y cada vez que cambie esta, se actualizan las propiedades de cada objeto Element.

/* ****************************************************************
* © JOAQUIN MARTINEZ RUS 2016
* PROYECTO:        Tabla periódica
* Archivo:         Element.cs
* Descripción:     Clase element.
*
* Historial:
*                  1. 12 sep 2016. Creación
*                  2. 03 oct 2016. Modificación. Se añade la propiedad
*                  Temperature para implementación de estados variables
*                  en función de la temperatura
*
* Comentarios:      Elemento de la tabla periódica
*
*
*******************************************************************/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel;

namespace DimitriMendeleyev
{
    public class Element: INotifyPropertyChanged
    {
        public string Symbol { get; set; }
        public string Name { get; set; }
        public int AtomicNumber { get; set; }
        public double AtomicMass { get; set; }
        public double Density { get; set; }
        public string Valencia { get; set; }
        public double MeltingPoint { get; set; }
        public double BoilingPoint { get; set; }
        public string StandarState { get; set; }
        public string SerialChemical { get; set; }
        public int Period { get; set; }
        public int Group { get; set; }
        private double temperature;

        public double Temperature
        {
            get { return temperature; }
            set
            {
                temperature = value;
                OnPropertyChanged("Temperature");
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        ///
<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));
            }
        }

    }
}

/* ****************************************************************
* © JOAQUIN MARTINEZ RUS 2016
* PROYECTO:        Tabla periódica
* Archivo:         PeriodicTable.cs
* Descripción:     Clase PeriodicTable.
*
* Historial:
*                  1. 12 sep 2016. Creación
*                  2. 03 oct 2016. Modificación. Se añade la propiedad
*                  Temperature para implementación de estados variables
*                  en función de la temperatura
*
* Comentarios:     Tabla periódica
*
*
*******************************************************************/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel;

namespace DimitriMendeleyev
{
    public class PeriodicTable: INotifyPropertyChanged
    {
        public PeriodicTable()
        {
            Elements = new List();
            SerialChemical = new List();
            SerialChemical.AddRange(new string[] { "Metales", "Metaloides" , "Otros no metales",
                "Halógenos", "Alcalinotérreos", "Alcalinos", "Metales de Transición",
                "Gases nobles", "Lantánidos", "Actínidos"});
            States = new List();
            States.AddRange(new string[] { "Gas", "Líquido", "Sólido", "Artificial" });
        }

        public List Elements { get; set; }
        public List SerialChemical { get; set; }
        public List States { get; set; }
        private double temperature;

        public double Temperature
        {
            get { return temperature; }
            set
            {
                temperature = value;
                setTemperatures();
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        ///
<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));
            }
        }

        void setTemperatures()
        {
            foreach (var element in Elements)
            {
                element.Temperature = this.Temperature;
            }
        }
    }
}

el código XAML del control es

        <TextBlock x:Name="Temp" Grid.Column="4" Grid.Row="8" Grid.RowSpan="2" Text="{Binding ElementName=slider, Path=Value, StringFormat='{}{0:0}°C'}" FontSize="40" TextAlignment="Center" VerticalAlignment="Top" Grid.ColumnSpan="3"/>
        <Slider x:Name="slider" Grid.ColumnSpan="12" Grid.Column="7" Grid.RowSpan="2"
                HorizontalAlignment="Stretch" VerticalAlignment="Top" Height="30" Margin="10"
                Grid.Row="8" Minimum="-2500" Maximum="6000" Value="{Binding Temperature, Mode=TwoWay}"
                Ticks="1" TickFrequency="15" SmallChange="1"/>

Y el resultado es el siguiente

TablaPeriodica.gif

Con esto debemos aprender varias cosas, la primera es que un código bien definido inicialmente, nos va a permitir realizar modificaciones sobre nuestra aplicación con facilidad y por otra parte, aprovecharnos de las condiciones que WPF nos brinda, nos permite seguir manteniendo aplicaciones robustas con los menos BUG,s posibles aunque en este caso de la tabla periódica, la sencillez de la aplicación no creo que sea un buen ejemplo, no obstante nunca hay que fiarse.

Una vez más, saludos

Tabla periódica

Tabla periódica
Con este post, vamos a mejorar nuestro aprendizaje sobre el diseño de software MVVM (Model View ViewModel) en las que claramente separamos el código por capas en el mismo ensamblado o en varios, pero en este caso lo haremos en el mismo. Para el ejemplo se me ha ocurrido mostrar la tabla periódica, en la cual no vamos a realizar modificaciones, puesto que los datos son los que son y lo único que nos interesa de ella es mostrarla correctamente.
tabla03
Como ya he comentado en otros posts, esto nos va a permitir diseñar y desarrollar un software mucho más robusto y fácil de mantener.
Las aplicaciones MVVM, son sucesoras de las aplicaciones MVC (Model View Controller). En una aplicación MVC podemos apreciar tres tipos de objetos:
Modelo
Esta capa, es la que se encarga de trabajar con los datos y contiene la capa de negocio.
Vista
La vista es la representación de cara al usuario o el objeto responsable de mostrar y permitir la interacción el usuario sobre los datos
Controlador
Permite transferir los datos desde el modelo a la vistaEsto nos permite poder sustituir o modificar cualquiera de las capas sin afectar o afectando lo menos posible al resto de capas, pudiendo mantener de un modo más eficaz nuestra aplicación. La diferencia con las aplicaciones WPF (o Silverlight) es que la capa ViewModel o modelo-vista es la responsable de exponer los datos del modelo usando bindings y de controlar el comportamiento de la vista

En nuestro caso, usamos como datos un archivo XML que he convertido a este formato desde una hoja de cálculo que tenía con los elementos de la tabla periódica y sus propiedades, como modelo, un gestor de datos desde XML, como vista la tabla periódica en XAML y como ViewModel, cada uno de los elementos de la tabla y la tabla en sí.

Los datos xml están distribuidos mediante un elemento raíz elements y como nodos hijos element los cuales contienen las propiedades en bruto que usará el ViewModel.

<elements xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <element>
    <simbolo>H</simbolo>
    <elemento>Hidrogeno</elemento>
    <numeroAtomico>1</numeroAtomico>
    <pesoAtomico>1,0079</pesoAtomico>
    <densidad>0,071</densidad>
    <valencia>1</valencia>
    <puntoEbullicion>-252,7</puntoEbullicion>
    <puntoFusion>259,2</puntoFusion>
    <estado>Gas</estado>
    <serieQuimica>Otros no metales</serieQuimica>
    <grupo>1</grupo>
    <periodo>1</periodo>
  </element>
  ...
</elements>

Los datos xml son extraídos y pasados a la clase ViewModel mediante la clase XMLManager. Esta clase extrae los datos del archivo XML y al mismo tiempo los convierte en el objeto de la clase ViewModel mediante XML.Linq. Fijaros en la línea subrayada donde desde Linq se crea el nuevo objeto Element por cada una de los componentes del archivo xml y que posteriormente la query se añade a la lista de retorno.

        public List GetElements()
        {
            string path = GetPathXML();
            List elements = new List();

            XDocument xdoc = XDocument.Load(path);

            var query = from u in xdoc.Descendants("element")
                        select new Element
                        {
                            Symbol = u.Element("simbolo").Value,
                            Name = u.Element("elemento").Value,
                            AtomicNumber = Convert.ToInt32(u.Element("numeroAtomico").Value),
                            AtomicMass = Convert.ToDouble(u.Element("pesoAtomico").Value),
                            Density = Convert.ToDouble(u.Element("densidad").Value),
                            Valencia = u.Element("valencia").Value,
                            MeltingPoint = !string.IsNullOrEmpty(u.Element("puntoEbullicion").Value) ? Convert.ToDouble(u.Element("puntoEbullicion").Value): 0,
                            BoilingPoint = !string.IsNullOrEmpty(u.Element("puntoFusion").Value) ? Convert.ToDouble(u.Element("puntoFusion").Value):0,
                            StandarState = u.Element("estado").Value,
                            SerialChemical = u.Element("serieQuimica").Value,
                            Group = Convert.ToInt32(u.Element("grupo").Value),
                            Period = Convert.ToInt32(u.Element("periodo").Value)
                        };

            elements.AddRange(query.ToList());
            return elements;
        }

Como clases ViewModel tenemos por una parte la clase Element que ostenta la entidad de cualquier elemento de la tabla y la clase PeriodicTable que alberga las colecciones. Con extremada sencillez, solo propiedades, sin métodos, de hecho no tenemos que usar la actualización de la propiedades mediante el evento PropertyChanged, ya que las propiedades de los elementos solo se van a cargar una sola vez.

Clase Element

/* ****************************************************************
* © JOAQUIN MARTINEZ RUS 2016
* PROYECTO:        Tabla periódica
* Archivo:         Element.cs
* Descripción:     Clase element.
 *
* Historial:       1. 12 sep 2016. Creación
*
* Comentarios:      Elemento de la tabla periódica
*
*
*******************************************************************/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel;

namespace DimitriMendeleyev
{
    public class Element: INotifyPropertyChanged
    {
        public string Symbol { get; set; }
        public string Name { get; set; }
        public int AtomicNumber { get; set; }
        public double AtomicMass { get; set; }
        public double Density { get; set; }
        public string Valencia { get; set; }
        public double MeltingPoint { get; set; }
        public double BoilingPoint { get; set; }
        public string StandarState { get; set; }
        public string SerialChemical { get; set; }
        public int Period { get; set; }
        public int Group { get; set; }

        public event PropertyChangedEventHandler PropertyChanged;

    }
}

Clase PeriodicTable

/* ****************************************************************
* © JOAQUIN MARTINEZ RUS 2016
* PROYECTO:        Tabla periódica
* Archivo:         PeriodicTable.cs
* Descripción:     Clase PeriodicTable.
 *
* Historial:
*                  1. 12 sep 2016. Creación
*
* Comentarios:     Tabla periódica
*
*
*******************************************************************/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel;

namespace DimitriMendeleyev
{
    public class PeriodicTable: INotifyPropertyChanged
    {
        public PeriodicTable()
        {
            Elements = new List<Element>();
            SerialChemical = new List<string>();
            SerialChemical.AddRange(new string[] { "Metales", "Metaloides" , "Otros no metales",
                "Halógenos", "Alcalinotérreos", "Alcalinos", "Metales de Transición",
                "Gases nobles", "Lantánidos", "Actínidos"});
            States = new List<string>();
            States.AddRange(new string[] { "Gas", "Líquido", "Sólido", "Artificial" });
        }

        public List<Element> Elements { get; set; }
        public List<string> SerialChemical { get; set; }
        public List<string> States { get; set; }

        public event PropertyChangedEventHandler PropertyChanged;
    }
}

Por último en la vista creo un control de usuario para un elemento de la tabla y que utilizaremos hasta en 108 ocasiones para crear nuestra tabla.
Cada elemento tiene una serie de propiedades como el número atómico, el símbolo, el nombre, la masa atómica, la serie química, el grupo, el periodo, el estado y un sinfín de propiedades. A este control definido, le asignamos mediante binding las propiedades que queremos mostrar y además le asigno la serie química mediante un color y el estado del elemento a 0ºC, (gas, sólido. líquido o artificial) mediante otro color. Para mostrar estos colores uso sendos conversores que a continuación muestro.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Data;
using System.Windows.Media;

namespace DimitriMendeleyev
{
    public class SerialChemicalToColorConverter : IValueConverter
    {

        #region IValueConverter

        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            SolidColorBrush scb = new SolidColorBrush();

            switch (value.ToString())
            {
                case "Metales":
                    scb.Color = Color.FromRgb(144,238,144);
                    break;
                case "Metaloides":
                    scb.Color = Color.FromRgb(126, 179, 126);
                    break;
                case "Otros no metales":
                    scb.Color = Color.FromRgb(47, 155, 47);
                    break;
                case "Halógenos":
                    scb.Color = Color.FromRgb(134, 221, 221);
                    break;
                case "Alcalinotérreos":
                    scb.Color = Brushes.Khaki.Color;
                    break;
                case "Alcalinos":
                    scb.Color = Brushes.Gold.Color;
                    break;
                case "Metales de Transición":
                    scb.Color = Brushes.BurlyWood.Color;
                    break;
                case "Gases nobles":
                    scb.Color = Brushes.SteelBlue.Color;
                    break;
                case "Lantánidos":
                    scb.Color = Brushes.SandyBrown.Color;
                    break;
                case "Actínidos":
                    scb.Color = Brushes.RosyBrown.Color;
                    break;
            }
            return scb;
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            return Color.FromRgb(255, 0, 0);
        }

        #endregion
    }

    public class StandarStateToColorConverter : IValueConverter
    {

        #region IValueConverter

        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            SolidColorBrush scb = new SolidColorBrush();

            switch (value.ToString())
            {
                case "Gas":
                    scb.Color = Brushes.LightBlue.Color;
                    break;
                case "Líquido":
                    scb.Color = Brushes.Blue.Color;
                    break;
                case "Sólido":
                    scb.Color = Brushes.Black.Color;
                    break;
                case "Artificial":
                    scb.Color = Brushes.DarkRed.Color;
                    break;
            }
            return scb;
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            return Color.FromRgb(255, 0, 0);
        }

        #endregion
    }
}

En cuanto al elemento, contiene un Grid con un control Button y un StackPanel con tres controles Textblock que mostrarán las propiedades mediante binding. Es de reseñar que el modo que usan los enlaces a datos es OneTime, ya que los datos solo se mostrarán una sola vez y no habrá modificaciones; con esto conseguimos menos sobrecarga sobre la aplicación y economía de medios y recursos.

<UserControl x:Class="DimitriMendeleyev.ElementControl"
             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:local="clr-namespace:DimitriMendeleyev"
             mc:Ignorable="d"
             xmlns:Controls="http://metro.mahapps.com/winfx/xaml/controls"
             d:DesignHeight="60" d:DesignWidth="60" x:Name="_element">
    <UserControl.Resources>
        <local:SerialChemicalToColorConverter x:Key="sctcc"/>
        <local:StandarStateToColorConverter x:Key="sstcc"/>
    </UserControl.Resources>
    <Grid>
        <Button Width="auto" Height="auto"
                Background="{Binding Path=SerialChemical, Converter={StaticResource sctcc}, Mode=OneTime}"
                x:Name="buttonElement">
            <StackPanel>
                <TextBlock x:Name="AtomicNumberTextBlock" Text="{Binding AtomicNumber, Mode=OneTime}" FontSize="12" TextAlignment="Center"/>
                <TextBlock x:Name="SymbolTextBlock" Text="{Binding Symbol, Mode=OneTime}" FontSize="15" TextAlignment="Center" FontWeight="Bold" Foreground="{Binding StandarState, Converter={StaticResource sstcc}}"/>
                <TextBlock x:Name="NameTextBlock" Text="{Binding Name, Mode=OneTime}" FontSize="10" TextAlignment="Center" />
            </StackPanel>
        </Button>
    </Grid>
</UserControl>

He creado otro control de usuario que muestra los datos ampliados del elemento de la tabla seleccionado y funciona prácticamente igual pero con la diferencia que se le pasa cada vez que se pulsa sobre un elemento concreto, este elemento, traspasa sus datos mediante el DataContext propio al control de datos ampliados.
Elemento con selección

<UserControl x:Class="DimitriMendeleyev.BigElementControl"
             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:local="clr-namespace:DimitriMendeleyev"
             mc:Ignorable="d"
             d:DesignHeight="220" d:DesignWidth="250" x:Name="big">
    <UserControl.Resources>
        <local:SerialChemicalToColorConverter x:Key="sctcc"/>
        <local:StandarStateToColorConverter x:Key="sstcc"/>
    </UserControl.Resources>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="200*"/>
            <ColumnDefinition Width="50*"/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="30"/>
            <RowDefinition Height="50"/>
            <RowDefinition Height="30"/>
            <RowDefinition Height="30"/>
            <RowDefinition Height="30"/>
            <RowDefinition Height="30*"/>
        </Grid.RowDefinitions>
        <Border BorderBrush="Navy" BorderThickness="4"
                Width="auto" Height="auto" Margin="1"
                Background="{Binding Path=SerialChemical, Converter={StaticResource sctcc}}"
                Grid.ColumnSpan="2" Grid.RowSpan="6"/>
        <TextBlock x:Name="AtomicNumberTextBlock" Text="{Binding AtomicNumber}" FontSize="20" TextAlignment="Center" Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2"/>
        <TextBlock x:Name="SymbolTextBlock" Text="{Binding Symbol}" FontSize="30" TextAlignment="Center" FontWeight="Bold" Grid.Row="1" Grid.Column="0" Foreground="{Binding StandarState, Converter={StaticResource sstcc}}" Grid.ColumnSpan="2"/>
        <TextBlock x:Name="NameTextBlock" Text="{Binding Name}" FontSize="20" TextAlignment="Center" Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2"/>
        <TextBlock Text="{Binding AtomicMass, StringFormat='Masa Atómica: {0}'}" FontSize="14" TextAlignment="Center" Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="2"/>
        <TextBlock Text="{Binding Density, StringFormat='Densidad: {0}'}" FontSize="14" TextAlignment="Center" Grid.Row="4" Grid.Column="0" Grid.ColumnSpan="2"/>
        <TextBlock Text="{Binding Valencia}" FontSize="14" TextAlignment="Center" Grid.Row="0" Grid.Column="1" Grid.RowSpan="4" TextWrapping="Wrap" Margin="5"/>
    </Grid>
</UserControl>

Paso del datacontext

        private void ButtonElement_Click(object sender, RoutedEventArgs e)
        {
            var buttonElement = e.OriginalSource as Button;
            var el = buttonElement.Parent as Grid;
            bigElement.DataContext = el.DataContext;
        }

La tabla periódica es la que tiene su “currito”, tenemos que crear un control ElementControl por cada uno de los elementos de la tabla colocándolos en su sitio correcto, dejándolo lo más bonito que nuestro gusto nos permita. El código que contiene esta vista, no es otro que la instanciación de la clase XMLManager, la clase PeriodicTable y asignación al DataContext de la lista de elementos que contiene esta última. Muestro a continuación el código XAML completo de la tabla periódica.

<Controls:MetroWindow x:Class="DimitriMendeleyev.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:Controls="http://metro.mahapps.com/winfx/xaml/controls"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:DimitriMendeleyev"
        mc:Ignorable="d"
        Title="MainWindow" Height="800" Width="1140"
                      BorderBrush="{DynamicResource AccentColorBrush}"
                      BorderThickness="1" WindowState="Maximized" WindowStartupLocation="CenterScreen">
    <Controls:MetroWindow.Resources>
        <Style x:Key="baseStyle" TargetType="TextBlock">
            <Setter Property="TextAlignment" Value="Center"/>
            <Setter Property="VerticalAlignment" Value="Center"/>
            <Setter Property="HorizontalAlignment" Value="Stretch"/>
            <Setter Property="Margin" Value="2"/>
            <Setter Property="FontSize" Value="22"/>
            <Setter Property="Foreground" Value="DarkGray"/>
        </Style>
        <Style x:Key="serialChemicalStyle" TargetType="TextBlock">
            <Setter Property="HorizontalAlignment" Value="Stretch"/>
            <Setter Property="TextAlignment" Value="Left"/>
            <Setter Property="VerticalAlignment" Value="Center"/>
            <Setter Property="Margin" Value="10"/>
            <Setter Property="FontSize" Value="12"/>
            <Setter Property="TextWrapping" Value="Wrap"/>
            <Setter Property="Width" Value="auto"/>
        </Style>
        <local:SerialChemicalToColorConverter x:Key="sctcc"/>
        <local:StandarStateToColorConverter x:Key="sstcc"/>
    </Controls:MetroWindow.Resources>
    <Grid x:Name="grid" HorizontalAlignment="Center">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="40"/>
            <ColumnDefinition Width="60"/>
            <ColumnDefinition Width="60"/>
            <ColumnDefinition Width="60"/>
            <ColumnDefinition Width="60"/>
            <ColumnDefinition Width="60"/>
            <ColumnDefinition Width="60"/>
            <ColumnDefinition Width="60"/>
            <ColumnDefinition Width="60"/>
            <ColumnDefinition Width="60"/>
            <ColumnDefinition Width="60"/>
            <ColumnDefinition Width="60"/>
            <ColumnDefinition Width="60"/>
            <ColumnDefinition Width="60"/>
            <ColumnDefinition Width="60"/>
            <ColumnDefinition Width="60"/>
            <ColumnDefinition Width="60"/>
            <ColumnDefinition Width="60"/>
            <ColumnDefinition Width="60"/>
            <ColumnDefinition Width="20"/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="80"/>
            <RowDefinition Height="60"/>
            <RowDefinition Height="60"/>
            <RowDefinition Height="60"/>
            <RowDefinition Height="60"/>
            <RowDefinition Height="60"/>
            <RowDefinition Height="60"/>
            <RowDefinition Height="60"/>
            <RowDefinition Height="20"/>
            <RowDefinition Height="60"/>
            <RowDefinition Height="60"/>
            <RowDefinition Height="60"/>
            <RowDefinition Height="20"/>
        </Grid.RowDefinitions>
        <!-- CONTROL GRANDE -->
        <local:BigElementControl x:Name="bigElement" Grid.Column="0" Grid.Row="9" Grid.ColumnSpan="3" Grid.RowSpan="3" Margin="10,0,0,0"/>
        <!-- Periodos -->
        <TextBlock Text="1" Grid.Column="0" Grid.Row="1" Style="{StaticResource baseStyle}"/>
        <TextBlock Text="2" Grid.Column="0" Grid.Row="2" Style="{StaticResource baseStyle}"/>
        <TextBlock Text="3" Grid.Column="0" Grid.Row="3" Style="{StaticResource baseStyle}"/>
        <TextBlock Text="4" Grid.Column="0" Grid.Row="4" Style="{StaticResource baseStyle}"/>
        <TextBlock Text="5" Grid.Column="0" Grid.Row="5" Style="{StaticResource baseStyle}"/>
        <TextBlock Text="6" Grid.Column="0" Grid.Row="6" Style="{StaticResource baseStyle}"/>
        <TextBlock Text="7" Grid.Column="0" Grid.Row="7" Style="{StaticResource baseStyle}"/>
        <!-- Grupos -->
        <TextBlock Text="1" Grid.Column="1" Grid.Row="0" Style="{StaticResource baseStyle}"/>
        <TextBlock Text="2" Grid.Column="2" Grid.Row="0" Style="{StaticResource baseStyle}"/>
        <TextBlock Text="3" Grid.Column="3" Grid.Row="0" Style="{StaticResource baseStyle}"/>
        <TextBlock Text="4" Grid.Column="4" Grid.Row="0" Style="{StaticResource baseStyle}"/>
        <TextBlock Text="5" Grid.Column="5" Grid.Row="0" Style="{StaticResource baseStyle}"/>
        <TextBlock Text="6" Grid.Column="6" Grid.Row="0" Style="{StaticResource baseStyle}"/>
        <TextBlock Text="7" Grid.Column="7" Grid.Row="0" Style="{StaticResource baseStyle}"/>
        <TextBlock Text="8" Grid.Column="8" Grid.Row="0" Style="{StaticResource baseStyle}"/>
        <TextBlock Text="9" Grid.Column="9" Grid.Row="0" Style="{StaticResource baseStyle}"/>
        <TextBlock Text="10" Grid.Column="10" Grid.Row="0" Style="{StaticResource baseStyle}"/>
        <TextBlock Text="11" Grid.Column="11" Grid.Row="0" Style="{StaticResource baseStyle}"/>
        <TextBlock Text="12" Grid.Column="12" Grid.Row="0" Style="{StaticResource baseStyle}"/>
        <TextBlock Text="13" Grid.Column="13" Grid.Row="0" Style="{StaticResource baseStyle}"/>
        <TextBlock Text="14" Grid.Column="14" Grid.Row="0" Style="{StaticResource baseStyle}"/>
        <TextBlock Text="15" Grid.Column="15" Grid.Row="0" Style="{StaticResource baseStyle}"/>
        <TextBlock Text="16" Grid.Column="16" Grid.Row="0" Style="{StaticResource baseStyle}"/>
        <TextBlock Text="17" Grid.Column="17" Grid.Row="0" Style="{StaticResource baseStyle}"/>
        <TextBlock Text="18" Grid.Column="18" Grid.Row="0" Style="{StaticResource baseStyle}"/>

        <!-- series quimicas-->
        <Grid Grid.Column="4" Grid.Row="1" Grid.ColumnSpan="9" Grid.RowSpan="3" Height="183" VerticalAlignment="Bottom">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="50"/>
                <ColumnDefinition Width="50"/>
                <ColumnDefinition Width="50"/>
                <ColumnDefinition Width="50"/>
                <ColumnDefinition Width="50"/>
                <ColumnDefinition Width="50"/>
                <ColumnDefinition Width="50"/>
                <ColumnDefinition Width="50"/>
                <ColumnDefinition Width="50"/>
                <ColumnDefinition Width="50"/>
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="35"/>
                <RowDefinition Height="35"/>
                <RowDefinition Height="35"/>
                <RowDefinition Height="35"/>
                <RowDefinition Height="35"/>
            </Grid.RowDefinitions>
            <!--Metaloides-->
            <Grid Grid.Row="0" Grid.Column="1" Grid.ColumnSpan="2" Grid.RowSpan="2" Margin="2,0,2,2">
                <Border Width="auto" Height="auto"  Background="{Binding Path=SerialChemical[1], Converter={StaticResource sctcc}}" CornerRadius="10,0,0,0"/>
                <TextBlock Text="{Binding Path=SerialChemical[1]}" Style="{StaticResource serialChemicalStyle}"/>
            </Grid>
            <!--No metales-->
            <Border Grid.Row="0" Grid.Column="3" Grid.ColumnSpan="6" BorderBrush="MidnightBlue" BorderThickness="2,2,2,0" CornerRadius="0,10,0,0" Margin="2"/>
            <TextBlock Text="No metales" HorizontalAlignment="Stretch" Grid.Row="0" Grid.Column="3" Grid.ColumnSpan="6" TextAlignment="Center" FontSize="19" VerticalAlignment="Top"/>
            <Grid Grid.Row="0" Grid.Column="3" Grid.ColumnSpan="2" Grid.RowSpan="2" Margin="2" VerticalAlignment="Bottom">
                <Border Width="auto" Height="auto"  Background="{Binding Path=SerialChemical[2], Converter={StaticResource sctcc}}" CornerRadius="0,0,0,8"/>
                <TextBlock Text="{Binding Path=SerialChemical[2]}" Style="{StaticResource serialChemicalStyle}" FontSize="10"/>
            </Grid>
            <Grid Grid.Row="0" Grid.Column="5" Grid.ColumnSpan="2" Grid.RowSpan="2" Margin="2" VerticalAlignment="Bottom">
                <Border Width="auto" Height="auto"  Background="{Binding Path=SerialChemical[3], Converter={StaticResource sctcc}}"/>
                <TextBlock Text="{Binding Path=SerialChemical[3]}" Style="{StaticResource serialChemicalStyle}"/>
            </Grid>
            <Grid Grid.Row="0" Grid.Column="7" Grid.ColumnSpan="2" Grid.RowSpan="2" Margin="2" VerticalAlignment="Bottom">
                <Border Width="auto" Height="auto" Background="{Binding Path=SerialChemical[7], Converter={StaticResource sctcc}}" CornerRadius="0,0,8,0"/>
                <TextBlock Text="{Binding Path=SerialChemical[7]}" Style="{StaticResource serialChemicalStyle}"/>
            </Grid>
            <!--Metales-->
            <Border Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="10" BorderBrush="MidnightBlue" BorderThickness="2,2,2,0" CornerRadius="10,10,0,0" Margin="2"/>
            <TextBlock Text="Metales" HorizontalAlignment="Stretch" Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="10" TextAlignment="Center" FontSize="19" VerticalAlignment="Top"/>

            <Grid Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="2" Grid.RowSpan="2" Margin="2">
                <Border Width="auto" Height="auto" Background="{Binding Path=SerialChemical[5], Converter={StaticResource sctcc}}" CornerRadius="0,0,0,10"/>
                <TextBlock Text="{Binding Path=SerialChemical[5]}" Style="{StaticResource serialChemicalStyle}"/>
            </Grid>
            <Grid Grid.Row="3" Grid.Column="2" Grid.ColumnSpan="2" Grid.RowSpan="2" Margin="2">
                <Border Width="auto" Height="auto" Background="{Binding Path=SerialChemical[4], Converter={StaticResource sctcc}}" />
                <TextBlock Text="{Binding Path=SerialChemical[4]}" Style="{StaticResource serialChemicalStyle}" FontSize="11"/>
            </Grid>
            <Grid Grid.Row="3" Grid.Column="4" Grid.ColumnSpan="2" Margin="2">
                <Border Width="auto" Height="auto" Background="{Binding Path=SerialChemical[8], Converter={StaticResource sctcc}}" />
                <TextBlock Text="{Binding Path=SerialChemical[8]}" Style="{StaticResource serialChemicalStyle}"/>
            </Grid>
            <Grid Grid.Row="4" Grid.Column="4" Grid.ColumnSpan="2" Margin="2">
                <Border Width="auto" Height="auto" Background="{Binding Path=SerialChemical[9], Converter={StaticResource sctcc}}" />
                <TextBlock Text="{Binding Path=SerialChemical[9]}" Style="{StaticResource serialChemicalStyle}"/>
            </Grid>
            <Grid Grid.Row="3" Grid.Column="6" Grid.ColumnSpan="2" Grid.RowSpan="2" Margin="2">
                <Border Width="auto" Height="auto" Background="{Binding Path=SerialChemical[6], Converter={StaticResource sctcc}}" />
                <TextBlock Text="{Binding Path=SerialChemical[6]}" Style="{StaticResource serialChemicalStyle}"/>
            </Grid>
            <Grid Grid.Row="3" Grid.Column="8" Grid.ColumnSpan="2" Grid.RowSpan="2" Margin="2">
                <Border Width="auto" Height="auto" Background="{Binding Path=SerialChemical[0], Converter={StaticResource sctcc}}" CornerRadius="0,0,10,0"/>
                <TextBlock Text="{Binding Path=SerialChemical[0]}" Style="{StaticResource serialChemicalStyle}"/>
            </Grid>

        </Grid>

        <!-- Elementos -->
        <Grid Grid.Column="2" Grid.Row="1">
            <Border BorderBrush="{DynamicResource AccentColorBrush}" BorderThickness="1" Width="auto" Height="auto" Margin="1"/>
            <StackPanel>
                <TextBlock x:Name="AtomicNumberTextBlock" Text="NºAtóm." FontSize="12" TextAlignment="Center"/>
                <TextBlock x:Name="SymbolTextBlock" Text="Símbolo" FontSize="14" TextAlignment="Center" FontWeight="Bold"/>
                <TextBlock x:Name="NameTextBlock" Text="Nombre" FontSize="12" TextAlignment="Center"/>
            </StackPanel>
        </Grid>
        <StackPanel Grid.Column="3" Grid.Row="1" Grid.RowSpan="2">
            <TextBlock Text="{Binding States[0]}" Foreground="{Binding States[0], Converter={StaticResource sstcc}}" FontSize="14" FontWeight="Bold" TextAlignment="Center"/>
            <TextBlock Text="{Binding States[1]}" Foreground="{Binding States[1], Converter={StaticResource sstcc}}" FontSize="14" FontWeight="Bold" TextAlignment="Center"/>
            <TextBlock Text="{Binding States[2]}" Foreground="{Binding States[2], Converter={StaticResource sstcc}}" FontSize="14" FontWeight="Bold" TextAlignment="Center"/>
            <TextBlock Text="{Binding States[3]}" Foreground="{Binding States[3], Converter={StaticResource sstcc}}" FontSize="14" FontWeight="Bold" TextAlignment="Center"/>
        </StackPanel>
        <local:ElementControl DataContext="{Binding Elements[0]}" Grid.Column="1" Grid.Row="1"/>
        <local:ElementControl DataContext="{Binding Elements[1]}" Grid.Column="18" Grid.Row="1"/>
        <local:ElementControl DataContext="{Binding Elements[2]}" Grid.Column="1" Grid.Row="2"/>
        <local:ElementControl DataContext="{Binding Elements[3]}" Grid.Column="2" Grid.Row="2"/>
        <local:ElementControl DataContext="{Binding Elements[4]}" Grid.Column="13" Grid.Row="2"/>
        <local:ElementControl DataContext="{Binding Elements[5]}" Grid.Column="14" Grid.Row="2"/>
        <local:ElementControl DataContext="{Binding Elements[6]}" Grid.Column="15" Grid.Row="2"/>
        <local:ElementControl DataContext="{Binding Elements[7]}" Grid.Column="16" Grid.Row="2"/>
        <local:ElementControl DataContext="{Binding Elements[8]}" Grid.Column="17" Grid.Row="2"/>
        <local:ElementControl DataContext="{Binding Elements[9]}" Grid.Column="18" Grid.Row="2"/>
        <local:ElementControl DataContext="{Binding Elements[10]}" Grid.Column="1" Grid.Row="3"/>
        <local:ElementControl DataContext="{Binding Elements[11]}" Grid.Column="2" Grid.Row="3"/>
        <local:ElementControl DataContext="{Binding Elements[12]}" Grid.Column="13" Grid.Row="3"/>
        <local:ElementControl DataContext="{Binding Elements[13]}" Grid.Column="14" Grid.Row="3"/>
        <local:ElementControl DataContext="{Binding Elements[14]}" Grid.Column="15" Grid.Row="3"/>
        <local:ElementControl DataContext="{Binding Elements[15]}" Grid.Column="16" Grid.Row="3"/>
        <local:ElementControl DataContext="{Binding Elements[16]}" Grid.Column="17" Grid.Row="3"/>
        <local:ElementControl DataContext="{Binding Elements[17]}" Grid.Column="18" Grid.Row="3"/>
        <local:ElementControl DataContext="{Binding Elements[18]}" Grid.Column="1" Grid.Row="4"/>
        <local:ElementControl DataContext="{Binding Elements[19]}" Grid.Column="2" Grid.Row="4"/>
        <local:ElementControl DataContext="{Binding Elements[20]}" Grid.Column="3" Grid.Row="4"/>
        <local:ElementControl DataContext="{Binding Elements[21]}" Grid.Column="4" Grid.Row="4"/>
        <local:ElementControl DataContext="{Binding Elements[22]}" Grid.Column="5" Grid.Row="4"/>
        <local:ElementControl DataContext="{Binding Elements[23]}" Grid.Column="6" Grid.Row="4"/>
        <local:ElementControl DataContext="{Binding Elements[24]}" Grid.Column="7" Grid.Row="4"/>
        <local:ElementControl DataContext="{Binding Elements[25]}" Grid.Column="8" Grid.Row="4"/>
        <local:ElementControl DataContext="{Binding Elements[26]}" Grid.Column="9" Grid.Row="4"/>
        <local:ElementControl DataContext="{Binding Elements[27]}" Grid.Column="10" Grid.Row="4"/>
        <local:ElementControl DataContext="{Binding Elements[28]}" Grid.Column="11" Grid.Row="4"/>
        <local:ElementControl DataContext="{Binding Elements[29]}" Grid.Column="12" Grid.Row="4"/>
        <local:ElementControl DataContext="{Binding Elements[30]}" Grid.Column="13" Grid.Row="4"/>
        <local:ElementControl DataContext="{Binding Elements[31]}" Grid.Column="14" Grid.Row="4"/>
        <local:ElementControl DataContext="{Binding Elements[32]}" Grid.Column="15" Grid.Row="4"/>
        <local:ElementControl DataContext="{Binding Elements[33]}" Grid.Column="16" Grid.Row="4"/>
        <local:ElementControl DataContext="{Binding Elements[34]}" Grid.Column="17" Grid.Row="4"/>
        <local:ElementControl DataContext="{Binding Elements[35]}" Grid.Column="18" Grid.Row="4"/>
        <local:ElementControl DataContext="{Binding Elements[36]}" Grid.Column="1" Grid.Row="5"/>
        <local:ElementControl DataContext="{Binding Elements[37]}" Grid.Column="2" Grid.Row="5"/>
        <local:ElementControl DataContext="{Binding Elements[38]}" Grid.Column="3" Grid.Row="5"/>
        <local:ElementControl DataContext="{Binding Elements[39]}" Grid.Column="4" Grid.Row="5"/>
        <local:ElementControl DataContext="{Binding Elements[40]}" Grid.Column="5" Grid.Row="5"/>
        <local:ElementControl DataContext="{Binding Elements[41]}" Grid.Column="6" Grid.Row="5"/>
        <local:ElementControl DataContext="{Binding Elements[42]}" Grid.Column="7" Grid.Row="5"/>
        <local:ElementControl DataContext="{Binding Elements[43]}" Grid.Column="8" Grid.Row="5"/>
        <local:ElementControl DataContext="{Binding Elements[44]}" Grid.Column="9" Grid.Row="5"/>
        <local:ElementControl DataContext="{Binding Elements[45]}" Grid.Column="10" Grid.Row="5"/>
        <local:ElementControl DataContext="{Binding Elements[46]}" Grid.Column="11" Grid.Row="5"/>
        <local:ElementControl DataContext="{Binding Elements[47]}" Grid.Column="12" Grid.Row="5"/>
        <local:ElementControl DataContext="{Binding Elements[48]}" Grid.Column="13" Grid.Row="5"/>
        <local:ElementControl DataContext="{Binding Elements[49]}" Grid.Column="14" Grid.Row="5"/>
        <local:ElementControl DataContext="{Binding Elements[50]}" Grid.Column="15" Grid.Row="5"/>
        <local:ElementControl DataContext="{Binding Elements[51]}" Grid.Column="16" Grid.Row="5"/>
        <local:ElementControl DataContext="{Binding Elements[52]}" Grid.Column="17" Grid.Row="5"/>
        <local:ElementControl DataContext="{Binding Elements[53]}" Grid.Column="18" Grid.Row="5"/>
        <local:ElementControl DataContext="{Binding Elements[54]}" Grid.Column="1" Grid.Row="6"/>
        <local:ElementControl DataContext="{Binding Elements[55]}" Grid.Column="2" Grid.Row="6"/>

        <local:ElementControl DataContext="{Binding Elements[71]}" Grid.Column="4" Grid.Row="6"/>
        <local:ElementControl DataContext="{Binding Elements[72]}" Grid.Column="5" Grid.Row="6"/>
        <local:ElementControl DataContext="{Binding Elements[73]}" Grid.Column="6" Grid.Row="6"/>
        <local:ElementControl DataContext="{Binding Elements[74]}" Grid.Column="7" Grid.Row="6"/>
        <local:ElementControl DataContext="{Binding Elements[75]}" Grid.Column="8" Grid.Row="6"/>
        <local:ElementControl DataContext="{Binding Elements[76]}" Grid.Column="9" Grid.Row="6"/>
        <local:ElementControl DataContext="{Binding Elements[77]}" Grid.Column="10" Grid.Row="6"/>
        <local:ElementControl DataContext="{Binding Elements[78]}" Grid.Column="11" Grid.Row="6"/>
        <local:ElementControl DataContext="{Binding Elements[79]}" Grid.Column="12" Grid.Row="6"/>
        <local:ElementControl DataContext="{Binding Elements[80]}" Grid.Column="13" Grid.Row="6"/>
        <local:ElementControl DataContext="{Binding Elements[81]}" Grid.Column="14" Grid.Row="6"/>
        <local:ElementControl DataContext="{Binding Elements[82]}" Grid.Column="15" Grid.Row="6"/>
        <local:ElementControl DataContext="{Binding Elements[83]}" Grid.Column="16" Grid.Row="6"/>
        <local:ElementControl DataContext="{Binding Elements[84]}" Grid.Column="17" Grid.Row="6"/>
        <local:ElementControl DataContext="{Binding Elements[85]}" Grid.Column="18" Grid.Row="6"/>

        <local:ElementControl DataContext="{Binding Elements[86]}" Grid.Column="1" Grid.Row="7"/>
        <local:ElementControl DataContext="{Binding Elements[87]}" Grid.Column="2" Grid.Row="7"/>

        <local:ElementControl DataContext="{Binding Elements[103]}" Grid.Column="4" Grid.Row="7"/>
        <local:ElementControl DataContext="{Binding Elements[104]}" Grid.Column="5" Grid.Row="7"/>
        <local:ElementControl DataContext="{Binding Elements[105]}" Grid.Column="6" Grid.Row="7"/>
        <local:ElementControl DataContext="{Binding Elements[106]}" Grid.Column="7" Grid.Row="7"/>
        <local:ElementControl DataContext="{Binding Elements[107]}" Grid.Column="8" Grid.Row="7"/>
        <local:ElementControl DataContext="{Binding Elements[108]}" Grid.Column="9" Grid.Row="7"/>
        <local:ElementControl DataContext="{Binding Elements[109]}" Grid.Column="10" Grid.Row="7"/>
        <local:ElementControl DataContext="{Binding Elements[110]}" Grid.Column="11" Grid.Row="7"/>
        <local:ElementControl DataContext="{Binding Elements[111]}" Grid.Column="12" Grid.Row="7"/>
        <local:ElementControl DataContext="{Binding Elements[112]}" Grid.Column="13" Grid.Row="7"/>
        <local:ElementControl DataContext="{Binding Elements[113]}" Grid.Column="14" Grid.Row="7"/>
        <local:ElementControl DataContext="{Binding Elements[114]}" Grid.Column="15" Grid.Row="7"/>
        <local:ElementControl DataContext="{Binding Elements[115]}" Grid.Column="16" Grid.Row="7"/>
        <local:ElementControl DataContext="{Binding Elements[116]}" Grid.Column="17" Grid.Row="7"/>
        <local:ElementControl DataContext="{Binding Elements[117]}" Grid.Column="18" Grid.Row="7"/>

        <!--  Lineas de lantánidos y actínidos -->
        <Polygon Grid.Column="3" Grid.Row="6" Grid.RowSpan="6" Stroke="Blue"
              Grid.ColumnSpan="18" Points="2 2 2 318 58 318 58 198 962 198 962 318 975 318 975 180 58 180 58 2">
            <Polygon.Fill>
                <SolidColorBrush Color="Coral" Opacity="0.3"/>
            </Polygon.Fill>
        </Polygon>
        <!--<local:ElementControl Grid.Column="3" Grid.Row="6" Background="Beige" />
        <local:ElementControl Grid.Column="3" Grid.Row="7" Background="PaleGoldenrod" />-->
        <TextBlock Text="57-71" Grid.Column="3" Grid.Row="6" Style="{StaticResource baseStyle}" FontSize="16"/>
        <TextBlock Text="89-103" Grid.Column="3" Grid.Row="7" Style="{StaticResource baseStyle}" FontSize="16"/>

        <local:ElementControl DataContext="{Binding Elements[56]}" Grid.Column="4" Grid.Row="10"/>
        <local:ElementControl DataContext="{Binding Elements[57]}" Grid.Column="5" Grid.Row="10"/>
        <local:ElementControl DataContext="{Binding Elements[58]}" Grid.Column="6" Grid.Row="10"/>
        <local:ElementControl DataContext="{Binding Elements[59]}" Grid.Column="7" Grid.Row="10"/>
        <local:ElementControl DataContext="{Binding Elements[60]}" Grid.Column="8" Grid.Row="10"/>
        <local:ElementControl DataContext="{Binding Elements[61]}" Grid.Column="9" Grid.Row="10"/>
        <local:ElementControl DataContext="{Binding Elements[62]}" Grid.Column="10" Grid.Row="10"/>
        <local:ElementControl DataContext="{Binding Elements[63]}" Grid.Column="11" Grid.Row="10"/>
        <local:ElementControl DataContext="{Binding Elements[64]}" Grid.Column="12" Grid.Row="10"/>
        <local:ElementControl DataContext="{Binding Elements[65]}" Grid.Column="13" Grid.Row="10"/>
        <local:ElementControl DataContext="{Binding Elements[66]}" Grid.Column="14" Grid.Row="10"/>
        <local:ElementControl DataContext="{Binding Elements[67]}" Grid.Column="15" Grid.Row="10"/>
        <local:ElementControl DataContext="{Binding Elements[68]}" Grid.Column="16" Grid.Row="10"/>
        <local:ElementControl DataContext="{Binding Elements[69]}" Grid.Column="17" Grid.Row="10"/>
        <local:ElementControl DataContext="{Binding Elements[70]}" Grid.Column="18" Grid.Row="10"/>

        <local:ElementControl DataContext="{Binding Elements[88]}" Grid.Column="4" Grid.Row="11"/>
        <local:ElementControl DataContext="{Binding Elements[89]}" Grid.Column="5" Grid.Row="11"/>
        <local:ElementControl DataContext="{Binding Elements[90]}" Grid.Column="6" Grid.Row="11"/>
        <local:ElementControl DataContext="{Binding Elements[91]}" Grid.Column="7" Grid.Row="11"/>
        <local:ElementControl DataContext="{Binding Elements[92]}" Grid.Column="8" Grid.Row="11"/>
        <local:ElementControl DataContext="{Binding Elements[93]}" Grid.Column="9" Grid.Row="11"/>
        <local:ElementControl DataContext="{Binding Elements[94]}" Grid.Column="10" Grid.Row="11"/>
        <local:ElementControl DataContext="{Binding Elements[95]}" Grid.Column="11" Grid.Row="11"/>
        <local:ElementControl DataContext="{Binding Elements[96]}" Grid.Column="12" Grid.Row="11"/>
        <local:ElementControl DataContext="{Binding Elements[97]}" Grid.Column="13" Grid.Row="11"/>
        <local:ElementControl DataContext="{Binding Elements[98]}" Grid.Column="14" Grid.Row="11"/>
        <local:ElementControl DataContext="{Binding Elements[99]}" Grid.Column="15" Grid.Row="11"/>
        <local:ElementControl DataContext="{Binding Elements[100]}" Grid.Column="16" Grid.Row="11"/>
        <local:ElementControl DataContext="{Binding Elements[101]}" Grid.Column="17" Grid.Row="11"/>
        <local:ElementControl DataContext="{Binding Elements[102]}" Grid.Column="18" Grid.Row="11"/>

    </Grid>
</Controls:MetroWindow>

Con esto, podemos apreciar que podríamos implementar lo que quisiéramos sobre la tabla periódica sin necesidad de modificar ni una sola línea de código de las clases MVVM. Como ejemplo, podríamos incluir en la tabla un control Slider que cambie la temperatura, de modo que una vez superara o quedara por debajo de las temperaturas de fusión y ebullición, el estado de los elementos cambiaría y por tanto el color del símbolo también.

Y aquí el resultado

Tabla05.gif
Espero que os haya gustado y como no, si queréis que os pase el proyecto, no dudéis en escribir un comentario o ponerse en contacto conmigo.

Saludos amig@s

Nota: Esta vez el NameSpace se lo dedico, como no, al químico Dimitri Mendeleiev que descubrió el fantástico patrón con el que los elementos químicos quedan ordenados en su tabla periódica. La imagen de la cabecera, es un monumento en la ciudad de Bratislava en su honor.