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:
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;
}
Ah - a Generic version (I notice that Disqus can't resist adding the closing tags to the class definition!). Thanks for that Kevin.
This code will be in the next release of the Bag of Tricks - https://github.com/thinkpixellab/bot/commit/d35e6c5bf73ee26f65a65f21c996e09a8efb1872
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...
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?
Wow... this was really helpful. Just what I was looking for. Thanks for posting it.
Thanks this is great! Help me a lot!
This implementation does not support MergedDictionaries !
I am absolutely amazed at how terrific the stuff is on this site.This is my first opportunity to visit this website.
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
Thanks for your code, very helpful to me!
www.vantel.net
Post a Comment