views:

24

answers:

1

I have a control for which i want to declare resources in a xaml file. if this was a user control i could put the resources in an <UserControl.Resources> block and reference them in the code via this.Resources["myResourceKey"] how do i achieve the same functionality in a control. at the moment the only link to xaml i have is through the controls static constructor, to reference the style (and control template)

static SlimlineSimpleFieldTextBlock() {
         DefaultStyleKeyProperty.OverrideMetadata(typeof(SlimlineSimpleFieldTextBlock), new FrameworkPropertyMetadata(typeof(SlimlineSimpleFieldTextBlock)));
}

but even if i add a block to the xaml <Style.Resources> I dont seem able to reference them (as the Style is null at the OnApplyTemplate stage) and even if i did it would mean if someone eles overrode the style i would lose my resources.

+1  A: 

Construct your resource key using ComponentResourceKey. Normal resource keys are searched for only up the visual tree and in your application resources. But any resource key that is a ComponentResourceKey is also searched for in the theme dictionary for the assembly containing the type. (This is also true for Type objects used as resource keys.)

In your Themes/Generic.xaml of the assembly containing a control called "Sandwich" you might have:

<SolidColorBrush x:Key="{ComponentResourceKey local:Sandwich, Lettuce}"
                 Color="#00FF00" />

<ControlTemplate x:Key="{ComponentResourceKey local:Sandwich, PeanutButter}" ...>
  ...
</ControlTemplate>

You can reference these resources in code like this:

var lettuce = (Brush)FindResource(
                 new ComponentResourceKey(typeof(Sandwich), "Lettuce"));

var penutButter = (ControlTemplate)FindResource(
                 new ComponentResourceKey(typeof(Sandwich), "PeanutButter"));

You can also refer to these resources in XAML like this:

<Border Background="{StaticResource ResourceKey={ComponentResourceKey local:Sandwich, Lettuce}}" />

Both of these forms of reference work from anywhere that FindResource can be used, which is inside the code or XAML for any object derived from FrameworkElement, FrameworkContentElement or Application.

Additional notes

The search algorithm for a ComponentResourceKey resource involves only the assembly contaning the specified type, not the type itself. Thus a control of type Soup could use a ComponentResourceKey of {ComponentResourceKey local:Sandwich,Seasonings} if the Soup and Sandwich classes were in the same assembly. As long as everything about the ComponentResourceKey matches exactly and the resource is actually in the same assembly as the given type, the resource will be found.

Also note that although it is possible to use pack URI to load a ResourceDictionary from another assembly, it is a bad idea to do so. Unlike the Themes/Generic.xaml solution you actually have to modify the application using your controls, and it also suffers from multiple-inclusion and overridability problems.

Whenever you are using Themes/Generic.xaml you must have your ThemeInfoAttribute set correctly on that assembly. You can start with this in your control library's AssemblyInfo.cs:

[assembly:ThemeInfoAttribute(ResourceDictionaryLocation.None, ResourceDictionaryLocation.SourceAssembly)]
Ray Burns
magic! the question then begs itself, what do you do if you want to access these resources from more than one control? do you end up using a packURI or can you just use them more than once from different controls?
Aran Mulholland
I have updated my answer with additional notes to make these this clearer. Enjoy!
Ray Burns
thanks a lot ray, this is a great definitive answer.
Aran Mulholland