Binding et UserControl (Part 2) : Le contenu

Suite à mon premier article sur le Binding dans les UserControls, on peut désormais s’attaquer au propriété de contenu !

Vous utilisez sans doute déjà des controles utilisant du contenu : Border avec la propriété Child (UIElement) , ContentControl avec la propriété Content (object).
Malheureusement, en l’état, notre UserControl ne peut en bénéficier.

Comment faire, alors, pour ajouter un contenu Header par exemple à notre UserControl ?
C’est très simple nous allons nous servir des Dependency properties et du ControlContent.

En reprenant notre UserControl AddressControl du premier article, commençons par implémenter via le snippet propdp la propriété Header coté C#. Celle-ci sera de type objet comme la propriété Content d’un ContentControl.

        /// <summary>
        /// Header
        /// </summary>

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

        // Using a DependencyProperty as the backing store for Header.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty HeaderProperty =
            DependencyProperty.Register("Header", typeof(object), typeof(AddressControl), new PropertyMetadata(null));

coté userControl XAML maintenant, positionnons le ContentControl chargé d’accueillir le contenu de notre Header.

<UserControl x:Class="SilverlightTest.AddressControl"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Width="400" Height="300">
    <Grid x:Name="LayoutRoot" Background="White">
        
        <ContentControl Content="{Binding Content}"></ContentControl>
        
        <TextBox Text="{Binding Address, Mode=TwoWay}"></TextBox>
        
    </Grid>
</UserControl>

Le Binding Content est effectué via la ligne Layout.DataContext = this que nous avions vu dans la premier partie.

On teste maintenant le controle :

        <local:AddressControl Address="Mon adresse">
             <local:AddressControl.Header>
                <Grid Background="Red">
                    <TextBlock Text="Hello"></TextBlock>
                </Grid>
             </local:AddressControl.Header>
        </local:AddressControl>

Cela fonctionne parfaitement : Le contenu de notre Header est bien positionné au dessus de notre TextBox d’adresse.

En revanche il reste encore un problème. Dès que l’on insère du Binding dans notre Contenu, celui-ci fonctionne un peu bizzarement :

        <local:AddressControl Address="Mon adresse">
             <local:AddressControl.Header>
                <Grid Background="{Binding Background}">
                    <TextBlock Text="Hello"></TextBlock>
                </Grid>
             </local:AddressControl.Header>
        </local:AddressControl>

ici le Binding du Background de notre contenu ne sera pas appliqué au DataContext du controle hébergant l’AddressControl mais a l’AdressControl lui même. En effet, dans le premier article nous avons rerouté le dataContext parent pour que celui ci soit remplacé par lui même. Il faut donc que le DataContext du ContentControl reprenne le DataContext d’origine.

Commencons pas donné un nom à notre UserControl AddressControl (MonUserControl par exemple):

<UserControl 
    x:Name="MonUserControl"
    x:Class="SilverlightTest.AddressControl"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Width="400" Height="300">
   ...
</UserControl>

Ensuite reroutons le DataContext vers celui d’origine tout en conservant le contenu vers le DataContexte du controle

<UserControl 
    x:Name="MonUserControl"
    x:Class="SilverlightTest.AddressControl"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Width="400" Height="300">
    <Grid x:Name="LayoutRoot" Background="White">
        
        <ContentControl DataContext="{Binding ElementName=LayoutRoot}" Content="{Binding Content,ElementName=LayoutRoot}"></ContentControl>
        
        <TextBox Text="{Binding Address, Mode=TwoWay}"></TextBox>
        
    </Grid>
</UserControl>

Le DataContext grâce à ElementName pointant vers MonUserControl est rerouté vers le DataContext du UserControl.
Le contenu de la propriété Content à pour source LayoutRoot (via ElementName) afin que le DataContext que l’on vient de fixer ne prenne pas le pas sur le Binding Header du UserControl.

On a, désormais, un contenu Header dans notre UserControl (et qui accepte le Binding) !

3 Responses to Binding et UserControl (Part 2) : Le contenu

  1.  

    Le soucis en donnant un nom au UserControl c’est qu’on ne peut pas avoir ce même UC plusieurs fois dans un même container 😉

  2. Tu as parfaitement raison Benjamin. Il faut donc rajouter un Grid encadrant le Grid LayoutRoot et le nommer MonUserControl et le tour est joué :)

     
  3. C’est corrigé dans le billet. Merci encore Benjamin.

     

leave your comment