Implementando UserControls

En XAML tenemos varias formas de escribir un código limpio y mantenible. Una de ellas es utilizar estilos, para ahorrarnos escribir una y otra vez los mismos valores y propiedades. Otra es el uso de DataTemplates, que nos permiten describir la estructura de un objeto con pelos y señales, pero aún tenemos una tercera, los UserControls.

Create a Custom user control for Windows 8 using C# and XAML

¿Qué es un UserControl?

Un UserControl viene a ser un control que encapsula a su vez a otros controles dándoles además, un comportamiento propio.

Dicho control puede recibir parámetros, pero la implementación de esta funcionalidad no es del todo simple, así que me he creado un par de clases Base para facilitarme el trabajo.

BaseUserControl

public class BaseUserControl : UserControl
{
    public event PropertyChangedEventHandler PropertyChanged;

    public BaseUserControl() : base()
    { }

    void SetValueDP(DependencyProperty property, object value,
        [System.Runtime.CompilerServices.CallerMemberName] String p = null)
    {
        SetValue(property, value);
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(p));
        }
    }
}

Esta clase limpia el cs del UserControl de la gestión del PropertyChanged.

Sin embargo, esta implementación es muy sencilla, y a veces necesito usar un Command y un CommandParameter en mis UserControls, para estos casos uso además la siguiente clase.

BaseCommandUserControl

public class BaseCommandUserControl : BaseUserControl
{
    public BaseCommandUserControl() : base()
    {
    }

    public static readonly DependencyProperty TagProperty =
        DependencyProperty.Register("Tag", typeof(object), typeof(BaseCommandUserControl), null);
        
    public static readonly DependencyProperty CommandProperty =
        DependencyProperty.RegisterAttached("Command", typeof(ICommand), typeof(BaseCommandUserControl), new PropertyMetadata(null, OnCommandPropertyChanged));

    public object Tag
    {
        get
        {
            return GetValue(TagProperty);
        }
        set
        {
            SetValue(TagProperty, value);
        }
    }

    public static void SetCommand(DependencyObject d, ICommand value)
    {
        d.SetValue(CommandProperty, value);
    }

    public static ICommand GetCommand(DependencyObject d)
    {
        return (ICommand)d.GetValue(CommandProperty);
    }

    protected static void OnCommandPropertyChanged(DependencyObject d,
        DependencyPropertyChangedEventArgs e)
    {
        var control = d as BaseCommandUserControl;
        if (control != null)
            control.Tapped += OnTapped;
    }

    private static void OnTapped(object sender, TappedRoutedEventArgs e)
    {
        var control = sender as BaseCommandUserControl;
        var command = GetCommand(control);

        command.Execute(control.Tag);
    }
}

De esta forma puedo, por ejemplo, enlazar un comando al Tap de la Grid principal del UserControl.

¿Y como sería la implementación del UserControl?

El CS del UserControl

public sealed partial class UCProfileButton : BaseCommandUserControl
{
    public static readonly DependencyProperty ContentLineProperty =
        DependencyProperty.Register("ContentLine", typeof(string), typeof(UCProfileButton), null);

    public static readonly DependencyProperty HeaderProperty =
        DependencyProperty.Register("Header", typeof(string), typeof(UCProfileButton), null);

    public static readonly DependencyProperty IconProperty =
        DependencyProperty.Register("Icon", typeof(SymbolIcon), typeof(UCProfileButton), null);
                
    public UCProfileButton() : base()
    {
        this.InitializeComponent();
        (this.Content as FrameworkElement).DataContext = this;
    }

    public string Header
    {
        get
        {
            return (string)GetValue(HeaderProperty);
        }
        set
        {
            SetValue(HeaderProperty, value);
        }
    }

    public string ContentLine
    {
        get
        {
            return (string)GetValue(ContentLineProperty);
        }
        set
        {
            SetValue(ContentLineProperty, value);
        }
    }

    public SymbolIcon Icon
    {
        get
        {
            return (SymbolIcon)GetValue(IconProperty);
        }
        set
        {
            SetValue(IconProperty, value);
        }
    }
}

En nuestro UserControl añadimos las propiedades estáticas que queramos actualizar pasando parámetros desde la vista.

Y finalmente el XAML.

El XAML del UserControl

<local:BaseCommandUserControl
    x:Class="Msw.Presentation.Apps.Windows8.UserControls.UCProfileButton"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:Msw.Presentation.Apps.Windows8.UserControls"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="300"
    d:DesignWidth="400">

    <Button Style="{StaticResource ButtonProfileStyle}" HorizontalAlignment="Center"
            Visibility="{Binding ContentLine, Converter={StaticResource nullOrEmptyVisibilityConverter}}">
        <Grid VerticalAlignment="Center" HorizontalAlignment="Center" Width="260">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto"/>
                <ColumnDefinition Width="*"/>
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
            </Grid.RowDefinitions>
            <AppBarButton Icon="{Binding Icon, Mode=OneWay}" IsCompact="True" Grid.RowSpan="2" IsHitTestVisible="False"/>
            <TextBlock Grid.Column="1" Text="{Binding Header, Mode=OneWay}" Style="{StaticResource SubheaderTextBlockStyle}" Foreground="White" />
            <TextBlock Grid.Column="1" Grid.Row="1" Text="{Binding ContentLine}" Style="{StaticResource BaseTextBlockStyle}" Foreground="White" />
        </Grid>
    </Button>
</local:BaseCommandUserControl>

Y ya tenemos nuestro UserControl listo para llamarlo desde la vista.

blog comments powered by Disqus