posté @ Friday, July 27, 2007 4:58 PM

Pour cette note, vouns noterez que je n'ai pas taggé le titre avec [WPF], mais seulement avec [Xaml]. En effet, Xaml en lui même n'est pas un composant de WPF, mais plutôt un langage utilisé et supporté par WPF.

Effectivement, si on n'y réfléchie bien, Xaml n'est qu'un langage de sérialisation / désérialisation de graphes d'objets.

Un autre composant très intéressant arrivé avec le Framework .Net 3.0 est le système des DependencyObject et DependencyProperty. Si vous n'êtes pas encore familiers avec ces termes, allez voir les "Dependency properties overview" et les "Attached properties overview". Ces 2 articles sont des "must-read" absolus pour tout développeur .Net sous Framework 3.0+.

Lorsque l'on crée des classes, dependency properties, attached properties etc. destinées à être sérialisées en Xaml, il peut être intéressant de contrôler cette sérialisation. Malheureusement, la documentation là-dessus est assez légère, et ce contrôle est assez souvent effectué par le biais de conventions de nommage et autres choses peu explicites.

Commençons par le plus simple: le contrôle de la sérialisation de propriété

public class SampleClass
    {
        // Read / write property : Serializable
        public int SerializableProperty { get; set; }
        // Read only property : non serializable
        public int ReadOnlyProperty { get; private set; }

        public void SetReadOnlyProperty(int val)
        {
            ReadOnlyProperty = val;
        }

        // Explicitly hidden property : non serializable
        [System.ComponentModel.DesignerSerializationVisibility(
            System.ComponentModel.DesignerSerializationVisibility.Hidden)]
        public int ExplicitNonSerializableProperty { get; set; }

        // Custom code controlled property : it depends :)
        public int ItDependsProperty { get; set; }

        public bool ShouldSerializeItDependsProperty()
        {
            return ItDependsProperty > 5;
        }
    }

Pour les 3 première propriétés, celà parait évident : une propriété en read-only ou avec l'attribut "DesignerSerializationVisibility" à "Hidden", on comprend que ce ne soit pas sérializable. Toutefois, je trouve le nom de l'attribut peu explicite : le sérializeur Xaml n'est pas forcément utilisé par un Designer...

Pour la 4e, ca se passe complètement par convention de nommage. En effet, le XamlWriter regarde si il trouve une méthode "ShouldSerialize[PropertyName]" retournant un booléen éxiste, et l'appelle pour savoir si il doit sérializer la propriété.

Au vu de cet exemple, on peut voir un problème: comment sérializer une propriété contenant une collection en lecture seule? Et bien pour celà, l'attribut "DesignerSerializationVisibility" peut prendre la valeur Content :

 

        // Non serializable
        public ObservableCollection<SampleClass> Children { get; private set; }

        // Serializable
        [System.ComponentModel.DesignerSerializationVisibility(
            System.ComponentModel.DesignerSerializationVisibility.Content)]
        public ObservableCollection<SampleClass> Children { get; private set; }

 

Avec cet attribut, le XamlWriter n'éssaiera pas de sérializer directement Children, mais de contenatera de sérializer son contenu (les éléments de la collection).

Mais alors, quid des Dependency Properties ?

Et bien c'est tout simple, pour les DependencyProperties, ca fonctionne par convention de nommage: le XamlWriter recherche des propriétés CLR ayant le même nom que la DependencyProperty, et se base sur les même regles (propriété en lecture / écriture, attribut, méthode ShouldSerialize).

 

Là où ca devient assez difficile, c'est pour les Attached properties. Pour rappel, les attached properties sont des propriétés que l'ont peut attacher dynamiquement à un DependencyObject. Un exemple courrant d'attached property est Grid.RowProperty. En effet, quand on utilise un Grid dans une application WPF, on retrouve souvent dans le Xaml, des choses du genre:

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition />
        </Grid.RowDefinitions>
        <Label Grid.Row="0" >This label is in the first row</Label>
        <Label Grid.Row="1">This label is in the second row</Label>
    </Grid>

Ainsi on attache à nos labels une propriété définie dans la classe Grid.

Pour contrôler la sérialization des AttachedProperty, voilà comment celà se passe :

 

    public class AttachedProperties : DependencyObject
    {
        // First : a serializable property

        public static int GetSerializableProperty(DependencyObject obj)
        {
            return (int)obj.GetValue(SerializablePropertyProperty);
        }

        public static void SetSerializableProperty(DependencyObject obj, int value)
        {
            obj.SetValue(SerializablePropertyProperty, value);
        }

        public static readonly DependencyProperty SerializablePropertyProperty =
            DependencyProperty.RegisterAttached("SerializableProperty", typeof(int),
                typeof(AttachedProperties), new UIPropertyMetadata(0));



        // Second : an explicitly non serializable property

        [System.ComponentModel.DesignerSerializationVisibility(
            System.ComponentModel.DesignerSerializationVisibility.Hidden)]
        public static int GetHiddenProperty(DependencyObject obj)
        {
            return (int)obj.GetValue(HiddenPropertyProperty);
        }

        public static void SetHiddenProperty(DependencyObject obj, int value)
        {
            obj.SetValue(HiddenPropertyProperty, value);
        }

        public static readonly DependencyProperty HiddenPropertyProperty =
            DependencyProperty.RegisterAttached("HiddenProperty", typeof(int),
                typeof(AttachedProperties), new UIPropertyMetadata(0));


        // Last : serialization controlled by code

        public static int GetItDepends(DependencyObject obj)
        {
            return (int)obj.GetValue(ItDependsProperty);
        }

        public static void SetItDepends(DependencyObject obj, int value)
        {
            obj.SetValue(ItDependsProperty, value);
        }

        public static bool ShouldSerializeItDepends(DependencyObject obj)
        {
            return GetItDepends(obj) > 5;
        }

        public static readonly DependencyProperty ItDependsProperty =
            DependencyProperty.RegisterAttached("ItDepends", typeof(int),
                typeof(AttachedProperties), new UIPropertyMetadata(0));

        
    }

Et voilà, maintenant vous savez comment gérer la sérialization Xaml de vos objet!

Mots clés Technorati : , , ,

 


Commentaires :

No comments posted yet.

Ecrire un commentaire :

Titre :*
Nom *
Email
Url
Commentaire : *  


Please add 3 and 2 and type the answer here: