dotNET
dotNET

Reputation: 35400

How to add more resources to a UserControl

Can we add new resources to a UserControl at the point of usage, without wiping out the resources that UserControl has defined itself?

So for exmaple, here is a UserControl:

<UserControl x:Class="MyControl">
  <UserControl.Resources>
    <!--Resource1-->
    <!--Resource2-->
  </UserControl.Resources>
</UserControl>

I use this control in the MainWindow:

<MainWindow>
  <local:MyControl>
    <local:MyControl.Resources>
      <!--Resource3-->
    </local:MyControl.Resources>
  </local:MyControl>
</MainWindow>

Doing this wipes out Resource1 and Resource2 and I'm left with Resource3 only. I have tried <ResourceDictionary.MergedDictionaries> too, that also has the same effect. I'm looking for a way for Resource3 to add to the existing resources list.

Upvotes: 5

Views: 4577

Answers (1)

Evk
Evk

Reputation: 101453

There is no built-in possibility to do that, as far as I know. But you can do that either via code, or with attached property. For example, let's define such property:

public static class ResourceExtensions {
    public static readonly DependencyProperty AdditionalResourcesProperty = DependencyProperty.RegisterAttached(
        "AdditionalResources", typeof(ResourceDictionary), typeof(ResourceExtensions), new PropertyMetadata(null, OnAdditionalResourcesChanged));

    private static void OnAdditionalResourcesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
        var fe = d as FrameworkElement;
        if (fe == null)
            throw new Exception("Cannot add resources to type " + d.GetType());
        if (fe.Resources == null)
            fe.Resources = new ResourceDictionary();
        var dict = e.NewValue as ResourceDictionary;
        if (dict != null) {                                
            foreach (DictionaryEntry resource in dict) {
                fe.Resources[resource.Key] = resource.Value;
            }
        }
    }

    public static void SetAdditionalResources(DependencyObject element, ResourceDictionary value) {
        element.SetValue(AdditionalResourcesProperty, value);
    }

    public static ResourceDictionary GetAdditionalResources(DependencyObject element) {
        return (ResourceDictionary) element.GetValue(AdditionalResourcesProperty);
    }
}

What it will do is take resource dictionary and copy all values from it to the resource dictionary of target control (overriding values of existing resources). Usage is:

<Window.Resources>
    <ResourceDictionary>
        <!-- This is resource dictionary to merge with target -->
        <ResourceDictionary x:Key="overrideResources">
            <Brush x:Key="foreground">Yellow</Brush>
        </ResourceDictionary>
    </ResourceDictionary>
</Window.Resources>
<wpfApplication1:UserControl1 wpfApplication1:ResourceExtensions.AdditionalResources="{StaticResource overrideResources}"/>

Note that for it to help with your another question you linked in comments - you need to use resources using DynamicResource extension, not StaticResource:

<UserControl.Resources>
    <Brush x:Key="foreground">Red</Brush>
    <Style x:Key="test" TargetType="TextBlock">
        <!-- Note DynamicResource here -->
        <Setter Property="Foreground" Value="{DynamicResource foreground}" />
    </Style>
</UserControl.Resources>
<StackPanel>
    <TextBlock Text="test" FontSize="12" Style="{StaticResource test}" />
</StackPanel>

If I apply the above method with attached property to this UserControl - text inside it will become yellow (was red), because Brush with key foreground was overriden but Style with key test was left intact, and I used DynamicResource. If I used StaticResource instead - resources in resource dictionary will still change, but control will not reflect that change, because with StaticResource it does not watch for changes in the resource.

Upvotes: 2

Related Questions