【问题标题】:How to add more resources to a UserControl如何向 UserControl 添加更多资源
【发布时间】:2017-03-18 11:55:22
【问题描述】:

我们能否在使用时向UserControl 添加新资源,而不会清除UserControl 自己定义的资源?

例如,这里是UserControl

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

我在MainWindow中使用这个控件:

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

这样做会清除 Resource1 和 Resource2,我只剩下 Resource3。我也试过&lt;ResourceDictionary.MergedDictionaries&gt;,也有同样的效果。我正在为 Resource3 寻找一种将添加到现有资源列表的方法。

【问题讨论】:

  • Resource3 应该在 Window.Resources 中。
  • @Clemens:我也试过了。问题是我需要覆盖(可以这么说)UserControl 中定义的特定资源的定义。这仅在我将新资源放在 MyControl.Resources 部分时才有效。把它放在其他任何地方似乎都没有效果。
  • @Clemens: This post 显示了我要解决的问题。

标签: wpf xaml wpf-controls resourcedictionary


【解决方案1】:

据我所知,没有内置的可能性可以做到这一点。但是您可以通过代码或附加属性来做到这一点。例如,让我们定义这样的属性:

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);
    }
}

它将做的是获取资源字典并将其中的所有值复制到目标控件的资源字典(覆盖现有资源的值)。用法是:

<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}"/>

请注意,为了帮助您解决您在 cmets 中链接的另一个问题 - 您需要使用使用 DynamicResource 扩展名的资源,而不是 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>

如果我将带有附加属性的上述方法应用于此UserControl - 其内部的文本将变为黄色(红色),因为带有键foregroundBrush 被覆盖但带有键Styletest 被覆盖完好无损,我使用了DynamicResource。如果我改用 StaticResource - 资源字典中的资源仍会更改,但控件不会反映该更改,因为使用 StaticResource 它不会监视资源中的更改。

【讨论】:

  • 谢谢。这似乎是目前唯一的出路。我接受你的回答。如果我们有任何改进,我们会进行审查。
  • 请注意,从代码中比从 xaml 中更容易。从代码中,您只需执行 'yourControl.Resources["Color1"] = Color.White' 并完成。但即便如此,使用 DynamicResource 而不是 StaticResource 也是相关的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-12-16
  • 1970-01-01
  • 2013-01-28
  • 1970-01-01
  • 2015-11-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多