Tuesday, 25 January 2011

A FindResource implementation for Silverlight

Here’s an interesting omission in Silverlight that I stumbled upon today: Silverlight doesn’t provide you with a way to do a hierarchical resource look-up in code.

You can do it in XAML. Write

<Grid>
    <Grid.Resources>
        <Style x:Key="MyStyle>
        </Style>
    </Grid.Resources>
    <DockPanel>
        <Button>
            <TextBlock Style="{StaticResource MyStyle}"/>
        </Button>
    </DockPanel>
</Grid>

and the TextBlock will find the appropriate style in the Grid’s resource dictionary, even though it’s several layers up the hierarchy. And if the resource isn’t found in any of the parent elements, StaticResource will then look in the Application’s Resource Dictionary.

But, whereas WPF provides the TryFindResource method to do the same kind of look-up in code, the best you can do in Silverlight is

element.ResourceDictionary[“MyStyle”]
and that returns null if the resource isn’t in the element’s own ResourceDictionary. 

No matter. It’s easy to a proper lookup yourself:

public static class FrameworkElementExtensions
{
    public static object TryFindResource(this FrameworkElement element, object resourceKey)
    {
        var currentElement = element;

        while (currentElement != null)
        {
            var resource = currentElement.Resources[resourceKey];
            if (resource != null)
            {
                return resource;
            }

            currentElement = currentElement.Parent as FrameworkElement;
        }

        return Application.Current.Resources[resourceKey];
    }
}

With that in place

textBox.TryFindResource("MyStyle");
will do what you expect.

Which of course makes me wonder why this was omitted from Silverlight in the first place. Am I missing something?

11 comments:

Kevin Moore said...

You might like this combination:

















public static bool TryFindResource(this FrameworkElement element, object resourceKey, out T value)
{
    while (element != null)
    {
        if (element.Resources.TryGetValue(resourceKey, out value))
        {
            return true;
        }
        element = element.Parent as FrameworkElement;
    }

    return Application.Current.Resources.TryGetValue(resourceKey, out value);
}

public static bool TryGetValue(this ResourceDictionary dictionary, object key, out T value)
{
    Contract.Requires(dictionary != null);
    if (dictionary.Contains(key))
    {
        var val = dictionary[key];
        if (val is T)
        {
            value = (T)val;
            return true;
        }
    }
    value = default(T);
    return false;
}

Samuel Jack said...

Ah - a Generic version (I notice that Disqus can't resist adding the closing tags to the class definition!). Thanks for that Kevin.

Kevin Moore said...

This code will be in the next release of the Bag of Tricks - https://github.com/thinkpixellab/bot/commit/d35e6c5bf73ee26f65a65f21c996e09a8efb1872

Kevin Moore said...

One should be careful with this implementation, though. If a resource with a given name exists but is of the wrong type you'll get a silent failure.

...wondering if the correct behavior is to avoid the type check and fail loudly instead of continuing to look...

Samuel Jack said...

By convention, an method named Try* shouldn't throw exceptions, so as things stand, returning false for a type mismatch is what you'd expect from your method. On the other hand, typing bugs can be pretty subtle and hard to spot, so it might be helpful to report why a resource wasn't found. Perhaps that's a case for returning a enum value rather than a bool?

Scott Simpson said...

Wow... this was really helpful. Just what I was looking for. Thanks for posting it.

Download Camstudio said...

Thanks this is great! Help me a lot!

Fabio Salvalai said...

This implementation does not support MergedDictionaries !

Silverlight Development said...

I am absolutely amazed at how terrific the stuff is on this site.This is my first opportunity to visit this website.

Sarah Lee said...

A people without the knowledge of their past history, origin and culture is like a tree without roots. See the link below for more info.


#origin
www.matreyastudios.com

Tayla Gusman said...

Thanks for your code, very helpful to me!

www.vantel.net

Post a Comment