TIP: Silverlight 5 Beta IMarkupExtension<T> and StaticExtension

17/05/2011

one comment

In WPF, there is the well known StaticExtension. However Silverlight users are struggling in finding quirky workarounds to this missing feature.

Now, in Silverlight 5, Microsoft introduced the IMarkupExtension<T> interface.
This one, not only reflects to the WPF MarkupExtension, but also is a generic interface rather than a class.
The MarkupExtension abstract class features only one method ProvideValue, this is a huge disadvantage, since you cannot inherit from multiple classes, and if you ever wanted to combine the MarkupExtension with another class (i.e. DependencyObject so you can set its properties with Data-Binding), this was impossible, whereas a type can implement multiple interfaces.

So let’s see some code.

XAML (m: refers to the namespace of the StaticExtension):

  <ContentControl                                                                                                                                 
    prism:RegionManager.RegionName="{Binding Source={m:Static MemberType=vm:RegionNames, Member=MainContent}}" />

C#:

using System.Xaml;
using System.ComponentModel;
using System.Globalization;
using System.Reflection;
 
namespace System.Windows.Markup
{
  /// <summary>
  /// Implements a markup extension that returns static field and property references.
  /// </summary>
  public class StaticExtension : MarkupExtension
  {
    // Fields
    private string _member;
    private Type _memberType;
 
    /// <summary>
    /// Initializes a new instance of the <see cref="System.Windows.Markup.StaticExtension"/> class.
    /// </summary>
    public StaticExtension()
    {
    }
 
    /// <summary>
    /// Initializes a new instance of the <see cref="System.Windows.Markup.StaticExtension"/> class
    /// using the provided member string.
    /// </summary>
    /// <param name="member">A string that identifies the member to make a reference to.
    /// This string uses the format prefix:typeName.fieldOrPropertyName. prefix is the mapping prefix for a XAML namespace,
    /// and is only required to reference static values that are not mapped to the default XAML namespace.</param>
    /// <exception cref="System.ArgumentNullException"><paramref name="member"/> is null.</exception>
    public StaticExtension(string member)
    {
      if (member == null)
      {
        throw new ArgumentNullException("member");
      }
      this._member = member;
    }
 
    private bool GetFieldOrPropertyValue(Type typestring nameout object value)
    {
      FieldInfo field = null;
      Type baseType = type;
      do
      {
        field = baseType.GetField(nameBindingFlags.Public | BindingFlags.Static);
        if (field != null)
        {
          value = field.GetValue(null);
          return true;
        }
        baseType = baseType.BaseType;
      }
      while (baseType != null);
      PropertyInfo property = null;
      baseType = type;
      do
      {
        property = baseType.GetProperty(nameBindingFlags.Public | BindingFlags.Static);
        if (property != null)
        {
          value = property.GetValue(nullnull);
          return true;
        }
        baseType = baseType.BaseType;
      }
      while (baseType != null);
      value = null;
      return false;
    }
 
    /// <summary>
    /// Returns an object value to set on the property where you apply this extension.
    /// For <see cref="System.Windows.Markup.StaticExtension"/>,
    /// the return value is the static value that is evaluated for the requested static member.
    /// </summary>
    /// <param name="serviceProvider">An object that can provide services for the markup extension.
    /// The service provider is expected to provide a service that implements a type resolver
    /// (<see cref="System.Windows.Markup.IXamlTypeResolver"/>).</param>
    /// <returns>The static value to set on the property where the extension is applied.</returns>
    /// <exception cref="System.InvalidOperationException">The member value for the extension
    /// is null at the time of evaluation.</exception>
    /// <exception cref="System.ArgumentException">Some part of the member string did not parse properly
    /// -or- <paramref name="serviceProvider"/> did not provide a service for <see cref="System.Windows.Markup.IXamlTypeResolver"/>
    /// -or- member value did not resolve to a static member.</exception>
    /// <exception cref="System.ArgumentNullException"><paramref name="serviceProvider"/> is null.</exception>
    public override object ProvideValue(IServiceProvider serviceProvider)
    {
      object obj2;
      if (this._member == null)
      {
        throw new InvalidOperationException("StaticExtension must have Member property set before ProvideValue can be called.");
      }
      Type memberType = this.MemberType;
      string str = null;
      string str2 = null;
      if (memberType != null)
      {
        str = this._member;
        str2 = memberType.FullName + "." + this._member;
      }
      else
      {
        str2 = this._member;
        int index = this._member.IndexOf('.');
        if (index < 0)
        {
          throw new ArgumentException(string.Format(
            "'{0}' StaticExtension value cannot be resolved to an enumeration, static field, or static property."this._member));
        }
        string qualifiedTypeName = this._member.Substring(0, index);
        if (qualifiedTypeName == string.Empty)
        {
          throw new ArgumentException(string.Format(
            "'{0}' StaticExtension value cannot be resolved to an enumeration, static field, or static property."this._member));
        }
        if (serviceProvider == null)
        {
          throw new ArgumentNullException("serviceProvider");
        }
        IXamlTypeResolver service = serviceProvider.GetService(typeof(IXamlTypeResolver)) as IXamlTypeResolver;
        if (service == null)
        {
          throw new ArgumentException(string.Format(
            "Markup extension '{0}' requires '{1}' be implemented in the IServiceProvider for ProvideValue.",
            base.GetType().Name,
            "IXamlTypeResolver"));
        }
        memberType = service.Resolve(qualifiedTypeName);
        str = this._member.Substring(index + 1, (this._member.Length - index) - 1);
        if (str == string.Empty)
        {
          throw new ArgumentException(string.Format(
            "'{0}' StaticExtension value cannot be resolved to an enumeration, static field, or static property."this._member));
        }
      }
      if (memberType.IsEnum)
      {
        return Enum.Parse(memberTypestrfalse);
      }
      if (!this.GetFieldOrPropertyValue(memberTypestrout obj2))
      {
        throw new ArgumentException(string.Format(
          "'{0}' StaticExtension value cannot be resolved to an enumeration, static field, or static property."str2));
      }
      return obj2;
    }
 
 
    /// <summary>
    /// Gets or sets a member name string that is used to resolve a static field or property
    /// based on the service-provided type resolver.
    /// </summary>
    /// <returns><see cref="System.String"/> that identifies the member to make a reference to.</returns>
    /// <exception cref="System.ArgumentNullException">Attempted to set
    /// <see cref="System.Windows.Markup.StaticExtension.Member"/> to null.</exception>    
    public string Member
    {
      get { return this._member; }
      set
      {
        if (value == null)
          throw new ArgumentNullException("value");
        this._member = value;
      }
    }
 
    /// <summary>
    /// Gets or sets the <see cref="System.Type"/> that defines the static member to return.
    /// </summary>
    /// <returns>The <see cref="System.Type"/> that defines the static member to return.</returns>
    /// <exception cref="ArgumentNullException">Attempted to set
    /// <see cref="System.Windows.Markup.StaticExtension.MemberType"/> to null.</exception>
    [DefaultValue((string)null)]
    public Type MemberType
    {
      get { return this._memberType; }
      set
      {
        if (value == null)
          throw new ArgumentNullException("value");
        this._memberType = value;
      }
    }
  }
}
Add comment
facebook linkedin twitter email

Leave a Reply

Your email address will not be published.

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

one comment

  1. Goran08/04/2012 ב 16:21

    Can StaticExtension be used in design time, as well as in runtime? It seems that it cannot resolve the type in design time, while in runtime everything works fine

    Reply