Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 494
dotNET Avatar asked Oct 30 '22 10:10

dotNET


1 Answers

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.

like image 72
Evk Avatar answered Dec 22 '22 21:12

Evk