Home > WPF > Reference to Self in XAML

Reference to Self in XAML

I’m always finding that I need to get a reference to the root element (this) in my XAML markup. If I’m in a situation that I can use a Binding, I can do something like this where I’m getting the Height of the current Window):

{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=Height}

However, there are many times that I need a reference to the root where I can’t use a binding. I really, really hope somebody will prove me wrong and show me there is a better way to do this that’s already built in to WPF, but since I haven’t been able to find anything, I added a MarkupExtension that uses reflection to get a reference to the private fields serviceProvider._Context._rootElement:

public class SelfExtension : MarkupExtension
{
    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        object self = null;
        Type selfType = serviceProvider.GetType();
        FieldInfo contextFieldInfo = selfType.GetField("_context", BindingFlags.NonPublic | BindingFlags.Instance);
        if(contextFieldInfo != null)
        {
            object context = contextFieldInfo.GetValue(serviceProvider);
            Type contextType = context.GetType();
            FieldInfo rootElementFieldInfo = contextType.GetField("_rootElement", BindingFlags.NonPublic | BindingFlags.Instance);
            if(rootElementFieldInfo != null)
                self = rootElementFieldInfo.GetValue(context);
        }
        return self;
    }
}

Yes, this is a terrible hack using reflection to access private fields, and there’s a good chance it will go away in a future version of WPF…hopefully because Microsoft builds in their own extension to do this properly. If you know of a better way, please speak up!

[Update: Microsoft addressed this is .NET 4]

Here is a new version that doesn’t require reflection using the updated API in .NET 4:

public class SelfExtension : MarkupExtension
{
    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        return ((System.Xaml.IRootObjectProvider)serviceProvider).RootObject;
    }
}
Advertisements
Categories: WPF Tags: , , , ,
  1. yuriy
    November 26, 2010 at 12:10 pm

    1. {Binding RelativeSource={RelativeSource Self}, Path=Height} is enough.

    2. if you still want to have MarkupExtension, the better way is to request IProvideValueTarget –
    var target = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget;
    var targetObject = target.TargetObject;

    • loosexaml
      November 26, 2010 at 12:54 pm

      I guess the title of the article is a little misleading, but if you follow the code snippets in the article, I’m actually trying to get a reference to the root element, not the element itself.

      If you can use a binding, you can use RelativeSource and find the ancestor of the appropriate type as in the first binding expression listed in the article. It’s trickier in cases where you can’t use a binding and have to use a markup extension. In that case, on .NET 3.0 and 3.5, the code I provided above seems to be the only option, using reflection. The snippet you’ve listed only provides access to the target object, *not* the root of the control tree.

      It turns out this was addressed in .NET 4, and reflection is no longer needed:

      object root = ((System.Xaml.IRootObjectProvider)serviceProvider).RootObject;

      • yuriy
        November 28, 2010 at 5:23 pm

        I think that the name of markup extension is misleading 🙂 It should by named RootExtension then.

  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: