Monthly Archives: January 2015

WPF Resource Key Collision

Our framework allows developers to create add-ins (modules), which are loaded at run time.  It’s possible for each developer to create custom resources and merge them into a Resource Dictionary on the Application object.  So what happens when two different add-ins define a resource with the same key?  How does WPF handle such key collision?

To answer this question, II created a small application which tests the ability of a single resource dictionary to load multiple merged dictionaries which have objects with same key. The code is below but I did discover that

  1. the app does not crash – WPF handles redifinition of the key gracefully
  2. the last one wins – whichever dictionary defining the entry with the same key loads last is the one that’s going to be resolved when app asks for such resource using the DynamicResource
  3. the original definition becomes effective when the override is removed – removing the duplicate definition makes the original value effective

It’s important to note that if the key already exists in the primary dictionary, the one into which other dictionaries are merged, the discovery rules will ignore any re-definition of the key, according to docs here.

Resources in a merged dictionary occupy a location in the resource lookup scope that is just after the scope of the main resource dictionary they are merged into. Although a resource key must be unique within any individual dictionary, a key can exist multiple times in a set of merged dictionaries. In this case, the resource that is returned will come from the last dictionary found sequentially in the MergedDictionaries collection. If the MergedDictionaries collection was defined in XAML, then the order of the merged dictionaries in the collection is the order of the elements as provided in the markup. If a key is defined in the primary dictionary and also in a dictionary that was merged, then the resource that is returned will come from the primary dictionary. These scoping rules apply equally for both static resource references and dynamic resource references.

<Application x:Class="Test.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             StartupUri="MainWindow.xaml">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="rd1.xaml"/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
</Application>
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:sys="clr-namespace:System;assembly=mscorlib"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    
    <sys:String x:Key="MyString">Hello</sys:String>
    
</ResourceDictionary>
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:sys="clr-namespace:System;assembly=mscorlib"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    
    <sys:String x:Key="MyString">Hello</sys:String>
    
</ResourceDictionary>
<Window x:Class="Test.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Button Click="HandleClick" Content="{DynamicResource MyString}"/>
    </Grid>
</Window>
using System;
using System.Windows;

namespace Test
{
    public partial class MainWindow : Window
    {
        private bool _remove;
        public MainWindow()
        {
            InitializeComponent();
        }

        private void HandleClick(object sender, RoutedEventArgs e)
        {
            if (_remove)
                Application.Current.Resources.MergedDictionaries.Remove(Application.Current.Resources.MergedDictionaries[1]);
            else
                Application.Current.Resources.MergedDictionaries.Add(new ResourceDictionary() { Source = new Uri("rd2.xaml", UriKind.Relative) });

            _remove = !_remove;
        }
    }
}