Поделиться через


Expandos in xaml

This post has a couple of suggestions on ways to accomplish expandos in Xaml. First some background …

 

On an HTML page you can define your own new “expando” properties on the fly, such as in this example:

 

<HTML>

  <BODY onload='paragraph1.innerText = paragraph1.testing'>

    <P ID='paragraph1' testing='1, 2, 3'/>

  </BODY>

</HTML>

 

Here I invented a new property named ‘testing’. I can both set that property and get it (the two parts in yellow above).

 

That’s a good feature when you want it. It’s a frustrating feature when you don’t. For example, in the following markup, I misspelled the “bgcolor” attribute. I’d like to be told that this is invalid, but instead it’s assumed to simply be an expando property.

 

<HTML>

  <BODY bgcolour='red' >

    <P>Hello</P>

  </BODY>

</HTML>

It’s for this reason that the Xaml language is strongly-typed. E.g. the following is a compile/load error (the “BGround” should be “Background”):

 

<TextBlock BGround='Red'>Hello</TextBlock>

But then if you do want an expando, how do you do it?

 

One way is to actually define the properties you want to use, as attached properties. For example, this markup uses the ‘MyProperties.Testing’ attached property to set a property onto an unsuspecting TextBlock:

 

<Window x:Class="WPFApplication1.Window1"

   xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"

   xmlns:local="clr-namespace:MyNamespace" >

      <TextBlock local:MyProperties.Testing='1, 2, 3' >Hello</TextBlock>

</Window>

 

.. where the code for MyProperties looks like this:

 

namespace MyNamespace

{

    public static class MyProperties

    {

        public static string GetTesting(FrameworkElement obj)

        {

            return (string)obj.GetValue(TestingProperty);

        }

        public static void SetTesting(FrameworkElement obj, string value)

        {

            obj.SetValue(TestingProperty, value);

        }

        // Using a DependencyProperty as the backing store for Testing. This enables animation, styling, binding, etc...

        public static readonly DependencyProperty TestingProperty =

            DependencyProperty.RegisterAttached("Testing", typeof(string), typeof(MyProperties) );

    }

}

 

 

If you don’t want to write any code, you can do it all in markup, though it takes more markup than the HTML case.

 

First of all, recognize that expandos are really just a property bag of key/value pairs. So in the first HTML example above the P element has a dictionary item that maps the key “testing” to the value ”1,2,3”. In WPF you can get a real dictionary using the built-in Hashtable class. (You can’t use the Dictionary<K,T> class, because Generics aren’t supported in Xaml.) There’s also a Tag property on all elements to hold your random data. Put those two together and you can do something like the following:

 

<Page

    xmlns='https://schemas.microsoft.com/winfx/2006/xaml/presentation'

    xmlns:x='https://schemas.microsoft.com/winfx/2006/xaml'

    xmlns:col='clr-namespace:System.Collections;assembly=mscorlib.dll'

    xmlns:sys='clr-namespace:System;assembly=mscorlib'

    Name='Page1' >

  <Page.Tag>

    <col:Hashtable>

      <sys:String x:Key='testing'>1, 2, 3</sys:String>

      <sys:String x:Key='anotherOne'>foo</sys:String>

      <sys:String x:Key='yetAnotherOne'>bar</sys:String>

    </col:Hashtable>

  </Page.Tag>

  <!-- This TextBlock.Text Binding queries a property out

       of the dictionary -->

  <TextBlock Text='{Binding Tag[testing], ElementName=Page1}' />

</Page>

 

 

Update: Another common use of expando properties is to “comment out” an attribute, since XML doesn’t provide a built-in means to do so. I was just reading an old post on Chuck’s blog, and he shows there a way to do this in Xaml by using the markup compatibility feature.

Comments