Como recordarán en versiones anteriores de Visual Studio se solia incluir en los templates la clase BindableBase
. Esta clase nos ayudaba a agilizar la creación de Modelos que hicieran Binding con la UI. Sin embargo esta clase desaparecio en versiones posteriores y si, a muchos nos hace falta.
En este artículo veremos como crearla, y como es mi costumbre lo haremos paso a paso para aprender.
Código fuente de este artículo

El código fuente completo de este artículo se encuentra disponible en GitHub.
https://github.com/JuanKRuiz/BindingDemo//DummyModel.cs
public class DummyModel
{
public bool EnableButton { get; set; }
public string TextoUnicaVez { get; set; }
public string TextNotificacionPorCambio { get; set; }
public string TextNotificacionBidireccional { get; set; }
public DummyModel()
{
EnableButton = true;
TextoUnicaVez = "Asignado por única vez";
TextNotificacionPorCambio = "Asignado cuando hay cambios en el modelo";
TextNotificacionBidireccional = "Cambiando el modelo ante cambios en la UI";
}
}
<!--MainPage.xaml-->
<Page
x:Class="BindingDemo.MainPage_INotify"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:BindingDemo"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:vm="using:BindingDemo.Models"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Page.DataContext >
<vm:DummyModel_INotify/>
</Page.DataContext>
<Page.Resources>
<Style TargetType="TextBox">
<Setter Property="FontSize" Value="30"/>
<Setter Property="Margin" Value="20"/>
</Style>
</Page.Resources>
<Grid>
<StackPanel>
<StackPanel>
<TextBox x:Name="txtUnicaVez"
PlaceholderText="Texto Unica Vez"
Text="{Binding TextoUnicaVez, Mode=OneTime}"/>
<TextBox x:Name="txtNotificacionPorCambio"
PlaceholderText="Text Notificacion Por Cambio"
Text="{Binding TextNotificacionPorCambio, Mode=OneWay}"/>
<TextBox x:Name="txtNotificacionBidireccional"
PlaceholderText="Text Notificacion Bidireccional"
Text="{Binding TextNotificacionBidireccional, Mode=TwoWay}"/>
<Button Margin="20" Padding="10"
FontSize="30"
IsEnabled="{Binding ElementName=tgsEnable, Path=IsOn, Mode=TwoWay}"
Click="Button_Click"
>Dummy Button</Button>
<ToggleSwitch x:Name="tgsEnable" Margin="20" Padding="10"
IsOn="{Binding EnableButton}"
FontSize="30">Bloquear Botón</ToggleSwitch>
</StackPanel>
</StackPanel>
</Grid>
</Page>

//MainPage.xaml.cs
private void Button_Click(object sender, RoutedEventArgs e)
{
var modelo = DataContext as DummyModel;
if (modelo != null)
{
modelo.TextNotificacionPorCambio = "Valor asignado al presionar botón";
}
}
Al ejecutar la aplicación y presionar el botón NO FUNCIONA!. Porque el modelo no puede enviar notificaciones de cambio a la UI.
##Notificación de cambios en el modelo con INotifyPropertyChanged
Creamos una copia de nuestra Vista `MainPage.xaml` y del modelo `DummyModel.cs` aquí haremos las modificaciones necesarias.
* DummyModel_INotify
* MainPage_INotify : Modificamos para que el DataContext sea ahora `DummyModel_INotify`
Debemos implementar la interfaz `INotifyPropertyChanged`, el runtine automáticamente busca si el modelo implementa la interfaz y la usa para cambiar la UI cuando cambie el modelo.
La implementación básica de `DummyModel_INotify` con `INotifyPropertyChanged` se ve así:
//DummyModel.cs
public class DummyModel_INotify : INotifyPropertyChanged
{
public bool EnableButton { get; set; }
public string TextoUnicaVez { get; set; }
public string TextNotificacionPorCambio { get; set; }
public string TextNotificacionBidireccional { get; set; }
public DummyModel_INotify()
{
EnableButton = true;
TextoUnicaVez = "Asignado por única vez";
TextNotificacionPorCambio = "Asignado cuando hay cambios en el modelo";
TextNotificacionBidireccional = "Cambiando el modelo ante cambios en la UI";
}
public event PropertyChangedEventHandler PropertyChanged;
}
Nótese que ninguna de las propiedades esta haciendo uso de esta funcionalidad así que el segundo paso es agregar la funcionalidad en las propidades que nos interesa.
Le aplicaremos esta funcionalidad al campo `TextNotificacionPorCambio`, para ello seguimos los siguientes pasos:
1. Convertirla a full property por lo cual requerimos definir un back store para la propiedad (\_textNotificacionPorCambio)
2. Al asignar la propiedad debemos llamar al evento de notificación definido anteriormente
3. Antes de llamar al evento **se debe** deberificar que este instanciado (podría no)
4. Como argumento hay que enviar el nombre de la propiedad actual
//fragmento DummyModel.cs
private string _textNotificacionPorCambio;
public string TextNotificacionPorCambio
{
get { return _textNotificacionPorCambio; }
set
{
_textNotificacionPorCambio = value;
if (PropertyChanged != null)
PropertyChanged(this,
new PropertyChangedEventArgs("TextNotificacionPorCambio")
);
}
}
Ahora hay que replicar esto mismo en las demás propiedades que queremos que notifiquen cambios, de inmediato notaremos que **el código de asignación es prácticamente idéntico entre unas y otras** así que para no hacerlo tan complejo podemos crear un método que incorpore toda esta lógica incluyendo las validaciones del evento y así eliminar el código redundante. Tengamos en cuenta:
1. La asignación del nuevo valor de la propiedad la podemos hacer pasando la propiedad por referencia, esto no solo nos permitirá escribir menos código sino que nos será útil más adelante.
2. Si bien para este caso podemos hacer uso de un tipo string para asignar la propiedad, podemos aprovechar y utilizar un tipo genérico, de tal forma que nos funcione sin importar el tipo de dato.
//fragmento DummyModel.cs
public void SetProperty<T>(ref T propertyBackStore, T newValue, string propertyName)
{
propertyBackStore = newValue;
if (PropertyChanged != null)
PropertyChanged(this,
new PropertyChangedEventArgs(propertyName)
);
}
###Podemos hacer más?
####Funcionalidad Genérica
Una muy buena optimización es que desde Framework 4.5 / WinRT podemos hacer uso del atributo CallerMemberName
el cual asigna cmo valor por defecto, a un parámetro de tipo cadena, el nombre del método/property que lo ha invocado. Esto nos es útil para evitar un parámetro en el llamado, quedando así:
//fragmento DummyModel.cs
public void SetProperty<T>(ref T propertyBackStore, T newValue,
[CallerMemberName] string propertyName = "")
{
propertyBackStore = newValue;
if (PropertyChanged != null)
PropertyChanged(this,
new PropertyChangedEventArgs(propertyName)
);
}
Solo notificar cuando se asigne un valor nuevo
Tal como esta la propiedad notifica los cambios incluso si el valor asignado es exactamente igual que el valor anterior. Valiéndonos de lo que ya tenemos montado podemos hacer esto:
- Solo disparar el evento si realmente se asignó un valor diferente
- Retornar true o false dependiendo de si la asignación se efectuó o no.
public bool SetProperty<T>(ref T propertyBackStore, T newValue,
[CallerMemberName] string propertyName = "")
{
if (Equals(propertyBackStore, newValue))
return false;
propertyBackStore = newValue;
if (PropertyChanged != null)
PropertyChanged(this,
new PropertyChangedEventArgs(propertyName)
);
return true;
}
La propiedad re-escrita y las demas propiedades que se requieren quedan de la siguiente forma
//fragmento DummyModel.cs
public string TextoUnicaVez { get; set; }
private bool _enableButton;
public bool EnableButton
{
get { return _enableButton; }
set
{
SetProperty(ref _enableButton, value);
}
}
private string _textNotificacionPorCambio;
public string TextNotificacionPorCambio
{
get { return _textNotificacionPorCambio; }
set
{
SetProperty(ref _textNotificacionPorCambio, value);
}
}
private string _textNotificacionBidireccional;
public string TextNotificacionBidireccional
{
get { return _textNotificacionPorCambio; }
set
{
SetProperty(ref _textNotificacionBidireccional, value);
}
}
Ejecutamos la nuestra Universal App y...
Notamos un par de cosas interesantes
- Al presionar Dummy Button el valor del segundo TextBox se actualiza automáticamente (ahora si), recordemos que no hemos modificado el objeto gráfico, pero hemos modificado el modelo al cual se encuentra 'bindeado'.
- Alternando la posición del ToggleSwitch modificamos el campo
EnableButton
del modelo - La propiedad IsEnable del Dummy Button desde un comienzo estaba haciendo Binding con la propiedad IsOn del ToggleSwitch así que al mover este último el botón cambia su estado, por si lo perdieron de vista ver líneas 3 y 7:
<Button Margin="20" Padding="10"
FontSize="30"
IsEnabled="{Binding ElementName=tgsEnable, Path=IsOn, Mode=TwoWay}"
Click="Button_Click"
>Dummy Button</Button>
<ToggleSwitch x:Name="tgsEnable" Margin="20" Padding="10"
IsOn="{Binding EnableButton}"
FontSize="30">Bloquear Botón</ToggleSwitch>
Notificación de cambios en el modelo con BindableBase
Como ven la solución anterior para Binding es por decir lo menos: "hermosa", pero aún podemos hacerlo mejor.
Esta solución puede convertirse en una completamente genérica, en la ya clásica BindableBase.
Así que procedemos a generalizar lo que hemos hecho en una clase de la cual futuros modelos puedan deribar.
Creamos una copia de nuestra Vista MainPage_INotify.xaml
y del modelo DummyModel_INotify.cs
aquí haremos las modificaciones necesarias, y adicionaré 2 constructores.
- DummyModel_Bindable
- MainPage_Bindable : Modificamos para que el DataContext sea ahora
DummyModel_Bindable
//BindableBase.cs
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace System.ComponentModel
{
public class BindableBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public bool SetProperty<T>(ref T propertyBackStore, T newValue,
[CallerMemberName] string propertyName = "")
{
if (Equals(propertyBackStore, newValue))
return false;
propertyBackStore = newValue;
if (PropertyChanged != null)
PropertyChanged(this,
new PropertyChangedEventArgs(propertyName)
);
return true;
}
public BindableBase()
{
}
public BindableBase(PropertyChangedEventHandler propertyChanged)
{
PropertyChanged = propertyChanged;
}
}
}
Ya una vez realizado esto, nuestro modelo queda así:
using System.ComponentModel;
namespace BindingDemo.Models
{
public class DummyModel_Bindable : BindableBase
{
public string TextoUnicaVez { get; set; }
private bool _enableButton;
public bool EnableButton
{
get { return _enableButton; }
set
{
SetProperty(ref _enableButton, value);
}
}
private string _textNotificacionPorCambio;
public string TextNotificacionPorCambio
{
get { return _textNotificacionPorCambio; }
set
{
SetProperty(ref _textNotificacionPorCambio, value);
}
}
private string _textNotificacionBidireccional;
public string TextNotificacionBidireccional
{
get { return _textNotificacionPorCambio; }
set
{
SetProperty(ref _textNotificacionBidireccional, value);
}
}
public DummyModel_Bindable()
{
EnableButton = true;
TextoUnicaVez = "Asignado por única vez";
TextNotificacionPorCambio = "Asignado cuando hay cambios en el modelo";
TextNotificacionBidireccional = "Cambiando el modelo ante cambios en la UI";
}
}
}
Al ejecutar la aplicación el resultado será el mismo, solo que ahora tenemos una BindableBase reutilizable en cualquiera de nuestros proyectos.
Quieres Aprender Más de Binding?
Estos videos están esperando por tí: