Solving the WPF Resource Key Collision

In my previous post about WPF Resource Keys, I talked about how WPF handles resource resolution when multiple resources with same key end up in loaded into same ResourceDictionary via merging.  In this post, I will present a solution.

So, apparently, Microsoft thought about it and provided a solution in a form of a class class called System.Windows.ComponentResourceKey.  This class is derives from a base class called System.Windows.ResourceKey, which is used inside the WPF resource resolution code.

So what does it do and how does work?

Normally, the WPF resource resolution logic assumes that all keys are objects used to identify resources in a ResourceDictionary.  Unlike a regular dictionary, a ResourceDictionary, technically, supports having multiple resources added with same key, but during the lookup operation, only the last resource added for a specific key will be returned.  So during the ‘merge’ operation, the new resources will overlay the existing ones if the keys clash, and during the ‘unmerge’ operation (ResourceDIctionary.MergedDictionaries.Remove()) the previously covered resource will emerge as ‘active’, so all the resources referenced via DynamicResouce will update.

The above gets more interesting if the key is of type System.Windows.ResourceKey.  If so, the resource resolution logic will only look at the resources which live in a dictionaries defined in the assembly referenced by the ResouceKey.  Moreover, when such set of dictionaries is found, the instance of the key is compared against other instances of the key to see if there’s a match, and because the System.Windows.ComponentResourceKey class overrides Equals and GetHashCode, the rules of equality take into consideration a bunch of parts, not just string value.

Here’s the code of the Equals and GetHashCode methods:

public override bool Equals(object o)
{
    var componentResourceKey = o as ComponentResourceKey;
    if (componentResourceKey == null)
    {
        return false;
    }
    // Ether neither objects define the TypeInTargetAssembly, 
    // or if both are defined, then make sure they are same
    if (!((componentResourceKey._typeInTargetAssembly != null) ? 
              componentResourceKey._typeInTargetAssembly.Equals(this._typeInTargetAssembly) :
             (this._typeInTargetAssembly == null)))
    {
        return false;
    }
    //If resourceId is null make sure they are both null
    if (componentResourceKey._resourceId == null)
    {
        return this._resourceId == null;
    }
    //otherwise do a simple equals
    return componentResourceKey._resourceId.Equals(this._resourceId);
}

public override int GetHashCode()
{  
   //use both, the type and the resouceid to generate a complex hashcode
   return ((this._typeInTargetAssembly != null) ? this._typeInTargetAssembly.GetHashCode() : 0) 
               ^ ((this._resourceId != null) ? this._resourceId.GetHashCode() : 0);
}

So when creating a reusable WPF component make sure you use the ComponentResourceKey class for your resource keys, do not just use strings and, obviously, refer to such resources using the ComponentResourceKey as well.  You can designate a single marker type (an empty public class) that lives in your assembly as a TypeInTargetAssembly to make it easier to reference your assembly when both, defining and referencing a resource.  This StackOverflow question showcases the use: http://stackoverflow.com/questions/337803/how-do-i-get-the-actual-resource-from-a-componentresourcekey.


//Exposing
public class Resources
{
    public static ComponentResourceKey BaseControlStyleKey
    {
        get
        {
            return new ComponentResourceKey(typeof(Resources), "BaseControlStyle");
        }
    }
}


//Using
myTextBox.Style = 
        Application.Current.TryFindResource(Resources.BaseControlStyleKey)
        as Style;



<!--Exposing -->
<Style x:Key="{ComponentResourceKey TypeInTargetAssembly={x:Type local:Resources}, ResourceId=BaseControlStyle}" TargetType="{x:Type FrameworkElement}">
    <Setter Property="Margin" Value="4,4,0,0" />
</Style>

<!-- Using -->
<TextBlock Style="{DynamicResource {ComponentResourceKey TypeInTargetAssembly={x:Type local:Resources}, ResourceId=BaseControlStyle}}"/>

<!-- Using in xaml by relying on the static property defined above -->
<TextBlock Style="{DynamicResource {x:Static local:Resources.BaseControlStyleKey}}"/>
Advertisements

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