Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Static and Type markup extensions for Silverlight 5 and WPF

0.00/5 (No votes)
22 May 2012 1  
An extended Static markup extension implementation for Silverlight 5 and WPF supporting invoking static methods with arguments, and a Silverlight Type markup extension implementation.

Introduction

This article presents a Silverlight 5 custom implementation of the Static and Type markup extension, already available in WPF. The Static markup extension is extended with support for calling static methods with parameters, in addition to accessing properties and fields. It also performs conversion to the target property type.

Background

Silverlight has a limited set of standard markup extensions compared to WPF. This means that standard WPF markup extensions like MultiBinding, x:Static, and x:Type are not available in Silverlight. However, starting with version 5 Silverlight supports custom markup extensions, which makes it possible to create extensions that can perform the same job. In a previous article, I showed how an extended Silverlight MultiBinding can be created, and in this article I present a Type and Static markup extension implementation for Silverlight. Just like my MultiBinding implementation, the Static markup extension also has extended functionality compared to the original WPF version. This Static implementation supports calling static methods with arguments, in addition to static properties and fields.

Using the code

All code examples below assume that you have included a reference to the SilverlightMarkupExtension assembly in the project and assigned the namespace prefix z to the SilverlightMarkupExtensions namespace in the XAML files.

Static markup extension

The Static markup extension can be used to refer to static members when assigning properties in XAML. The WPF x:Static extension supports public static properties and fields. One typical use is to refer to resources keys in typed resource files to localize content:

<TextBlock Text={x:Static demo:Resources.Title} />

where demo:Resources is a reference to the typed resource file and Title is the name of the resource property. The Silverlight Static implementation supports this in almost the same way:

<TextBlock Text={z:Static Member=demo:Resources.Title} /> 

Since Silverlight does not support positional arguments we have to specify the Member property in the declaration. In the same way as the original x:Static extension, you can also separate the type and the member like this:

<TextBlock Text={z:Static MemberType=demo:Resources, Member=Title} />

If you repeatedly want to refer to members in the same type, e.g., resources, this Static markup extension also supports specifying the member type once on the root XAML object (UserControl) by using the Static.DefaultMemberType attached property.

<UserControl  z:Static.DefaultMemberType=”demo:Resources” ><TextBlock Text={z:Static Member=Title /> 

Invoking static methods

In addition to support for accessing static properties and fields, like the WPF x:Static extension, the Silverlight Static markup extension supports invoking static methods as simple as this:

<TextBlock Text="{z:Static MemberType=demo:MyModelView, Member='GetSum(7,6.3)'}" />

In this example we are displaying the result of calling the public static MyModelView.GetSum method accepting two parameters. Using a comma-separated parameter list after the member name is all that is needed to invoke the static method. Basic type conversion of standard types using the IConvertible interface is supported. To invoke methods without parameters, just specify an empty pair of parentheses.

The Static extension also supports specifying arguments as separate properties, like this:

<ContentControl Content="{z:Static Member=demo:MyModelView.GetSum, Arg1={z:Static Member='demo:MyModelView.GetSum(6,7)'},Arg2=8}" />

As demonstrated above we can use this to pass in the results of markup extensions as arguments. The result shown in this case will be 21 (6+7+8). Up to three arguments is currently supported, and it is straightforward to add support for more arguments.

Type markup extension

In WPF the x:Type markup extension can be used to refer to named types when assigning properties in XAML, but this markup extension is not included in the Silverlight standard framework. On the other hand Silverlight 5 has built-in support for named types when assigning properties of type System.Type, so in most cases a Type markup extension is not required. It is only required when the property to be set to a type is not of type System.Type, but of its parent System.Object type. The Silverlight implementation here has the same properties as the WPF counterpart, Type and TypeName – only one of them can be used on the same declaration. The former property is of type System.Type and can be assigned a named type with an optional namespace prefix before. This is the preferred way of specifying the type since this is checked at design-time. In contrast, TypeName is a String property which is evaluated only in run-time, and must be set to a name of a type with the appropriate namespace prefix. TypeName can be useful in rare cases where the type name is specified as a XAML resource and referred to using a StaticResource markup extension, or some other markup extension provides the type name, for instance Static.

In the following example, we use the Type markup extension to pass in a type reference as an argument to a method to be invoked directly in XAML using the Static markup extension:

<ComboBox ItemsSource="{z:Static Member=demo:Sex.GetValues, Arg1={z:Type Type=demo:Sex}}" 
          SelectedItem="{z:Static Member=demo:Sex.Unknown}" />

Sex here is an enum type. We are invoking the Enum.GetValues static method with an argument specifying the type to return the values for. The result will be a combo box populated with the enum values. We also use Static to refer to a specific enum value, Unknown, when defining the initial selected item.

Use in WPF

Both the Static and Type markup extensions can be used in WPF XAML code, which makes it possible to transfer XAML written for Silverlight to WPF directly. The new Static implementation can also be useful in WPF XAML on its own thanks to its extended ability to invoke methods. Another advantage compared to the built-in x:Static is that the implementation for Silverlight tries to convert the result to the property type before returning it. This can be useful in situations where you have a resource file item defined as a String type but want to apply it to a numeric property. In the example below we have a resource TitleWidth, which must be String, assigned to the value "100" and tries to use it for the numeric property Width:

<ContentPresenter Content="{x:Static demo:Resources.Title}" Width="{x:Static demo:Resources.TitleWidth}" /> 

<ContentPresenter Content="{z:Static Member=demo:Resources.Title}" Width="{z:Static Member=demo:Resources.TitleWidth}" />

Using the built-in x:Static markup extension as in the first line will result in a run-time error saying “Set property 'System.Windows.FrameworkElement.Width' threw an exception. '100' is not a valid value for property 'Width'”. Simply replacing it with my z:Static markup extension will make it work, thanks to its automatic type conversion.

Limited design-time support

Unfortunately, when using the Static and Type markup extensions, the result will not be directly visible in the Visual Studio Silverlight designer, because the ProvideValue of a custom markup extension is not called in Silverlight design-mode. However, this is inconsistent with the behavior in the WPF designer where the ProvideValue is actually called. Anyhow, the services used by the Static and Type extension are not available in either WPF or Silverlight design-mode, making it impossible to resolve the values during design-time in most cases.

Some have utilized the (strange?) fact that the ToString() of the custom markup extension is called in the Silverlight design-mode to provide a design-time value. However, this seems to be true only in a ContentPresenter context and therefore not generally applicable to all properties.

Implementation details

Both markup extensions use the standard services available through the service provider passed in to the ProvideValue method. To resolve type names, possible with namespace prefixes, the IXamlTypeResolver service is used. The full implementation of Type’s ProvideValue method is shown below:

public override Object ProvideValue(IServiceProvider serviceProvider) 
{ 
   if (Type == null) 
   { 
      // Excluded some parameter checking
   
      IXamlTypeResolver resolver = 
        serviceProvider.GetService(typeof(IXamlTypeResolver)) as IXamlTypeResolver; 
      if (resolver == null) DependencyProperty.UnsetValue; 
      Type = resolver.Resolve(TypeName); 
   } 
   return Type; 
}

The Static markup extension also uses the IXamlTypeResolver service to resolve type names specified in the Member property. In addition it uses the IProvideValueTarget service to determine the target property type. In this way Static tries to convert the value returned from the invoked static method to the correct type:

// Try to convert return value to target property type. 
if (serviceProvider != null) 
{ 
    IProvideValueTarget pvt = 
      serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget; 
    if (pvt != null) 
    { 
        PropertyInfo targetProperty = pvt.TargetProperty as PropertyInfo; 
        if (targetProperty != null) 
        { 
            returnValue = ConvertToType(targetProperty.PropertyType, returnValue, 
                                        Thread.CurrentThread.CurrentUICulture); 
        } 
#if !SILVERLIGHT 
        else // In WPF a DependencyProperty is used for dependency properties. 
        { 
            DependencyProperty propertyMetaData = pvt.TargetProperty as  DependencyProperty; 
            if (propertyMetaData != null) 
            { 
                returnValue = ConvertToType(propertyMetaData.PropertyType, 
                                            returnValue, Thread.CurrentThread.CurrentUICulture); 
            } 
        } 
#endif 
    } 
}

Finally, the IRootObjectProvider service is used to look for DefaultMemberType attached properties set on the XAML root object, usually a UserControl:

// Look for Static.DefaultMemberType set on root object. 
if (serviceProvider != null) 
{ 
    IRootObjectProvider rop = 
      serviceProvider.GetService(typeof(IRootObjectProvider)) as IRootObjectProvider; 
    if (rop != null) 
    { 
        DependencyObject rootObject = rop.RootObject as DependencyObject; 
        if (rootObject != null) 
        { 
            memberType = GetDefaultMemberType(rootObject); 
        } 
    } 
}

Conclusions

This work has shown how to extend Silverlight 5 with markup extensions similar to the basic ones in WPF, and how to use the various services available. Together with the MultiBinding implementation presented before we are starting to get a small utility library with useful Silverlight markup extensions. Invoking static methods can be useful, but what we really want to have is the ability to invoke instance methods as well. Therefore I have started to create a MethodBinding markup extension based on MultiBinding. This will be the topic of my next article, but if you are curious you can already now have a preview of the work in the source code provided with this article.

History

  • February 19, 2012 – Initial version developed and tested with Silverlight 5 with Visual Studio 2010 SP1.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here