【问题标题】:Wpf merged resource dictionary no being recognized from app.xamlWpf 合并的资源字典无法从 app.xaml 中识别
【发布时间】:2013-06-12 04:57:50
【问题描述】:

我有一个 WPF .net 4.5 应用程序,在合并资源字典时遇到问题。

我有与This SO questionThis Question 完全相同的问题,但接受的解决方案对我不起作用。

我的 app.xaml 中声明了一个资源字典,如下所示(为清楚起见进行了简化):

<Application.Resources>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="Skin/ResourceLibrary.xaml" />              
            <ResourceDictionary Source="Skin/Brushes/ColorStyles.xaml" />               
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>               
</Application.Resources>

问题: 当 app.xaml 中列出时,应用程序可以“看到”ColorStyles 字典,但如果我将它移动/嵌套在 ResourceLibrary.xaml 中,则应用程序不会“看到”ColorStyles.xaml,并且会出现有关缺少静态资源的错误。

以下是我创建 ResourceLibrary.xaml 字典(简体)的方法:

<ResourceDictionary 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <ResourceDictionary.MergedDictionaries>

        <!--  BRUSHES AND COLORS  -->
        <ResourceDictionary Source="Brushes/ColorStyles.xaml" />

    </ResourceDictionary.MergedDictionaries>
</ResourceDictionary>

更改原因:我当前的资源字典组织很糟糕,我需要更改它(因为我不止一次地创建对象)。我想在“Skin”文件夹中有一个资源字典,然后是用于组织剩余样式字典的子文件夹,这些字典将全部合并到 ResourceLibrary.xaml 文件中,而该文件又将在 app.xaml 中调用。

我尝试了什么: 是的,我确实尝试使用上面链接中的解决方案:

<Application.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="Skin/ResourceLibrary.xaml"/>
        </ResourceDictionary.MergedDictionaries>
        <!-- Dummy Style, anything you won't use goes -->
        <Style TargetType="{x:Type Rectangle}" />
    </ResourceDictionary>
</Application.Resources>

但我在虚拟样式行收到以下错误:

错误 2 属性元素不能位于元素的中间 内容。它们必须在内容之前或之后。

将代码更改为以下代码摆脱了上面的错误,感谢 lisp 注释:

<Application.Resources>
    <ResourceDictionary>
        <!--Global View Model Locator-->
        <vm:ViewModelLocator x:Key="Locator" d:IsDataSource="True" />

        <!-- Dummy Style, anything you won't use goes -->
        <Style TargetType="{x:Type Rectangle}" />

        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="Skin/ResourceLibrary.xaml"></ResourceDictionary>             
            <ResourceDictionary Source="Skin/Brushes/ColorStyles.xaml" />
            <ResourceDictionary Source="Skin/NamedStyles/AlertStyles.xaml" />

        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>               
</Application.Resources>

但资源库仍未被调用。

我也尝试将所有文​​件路径更改为打包 URI,但这也没有解决问题。

我尝试将 resourceLibrary.xaml 和其他资源字典移动到不同的类库项目中(使用与上述相同的文件夹结构和文件)。然后我使用了以下 URI,但我仍然无法访问在 ResourceLibrary.xaml 文件中声明的资源。

<ResourceDictionary Source="pack://application:,,,/FTC.Style;component/ResourceLibrary.xaml" />

但同样,如果我将每个资源字典添加到 App.Xaml 文件中,使用上面的 UIR 格式,这些资源都是可用的。

错误消失了,但我仍然无法使用属于 ResourceLibrary.xaml 文件中合并字典一部分的资源。我倾向于同意 dowhilefor 关于我是否应该使用这种方法的评论,但我想弄清楚这一点,因为这个问题的最常见解决方案(参见本文顶部的链接)不起作用,也许这个解决方案可以帮助别人。

问题:为什么 ResourceLibrary.xaml 文件会被忽略?

【问题讨论】:

  • 您的虚拟样式相关的 xaml 是否与发布的完全一样?我没有Error 2,您的原始代码中可能有语法错误,请参阅this
  • @lisp 谢谢,我编辑了我的代码以显示实际发生的情况。我在合并字典的上方和下方都声明了元素。所以我更改了代码,但我仍然遇到 ResourceLibrary 没有被调用的问题
  • 我可以给你一个很好的提示:使用尽可能少的 xamls。不要试图通过拆分来过度组织它。我们用 500 个 xamls 完成了它,我仍然在晚上哭着和出汗醒来。您最多应该有 3 层。 app.xaml,其中包含所有资源字典、来自具有默认控件的库的 Generic.xaml,当然还有您在 app.xaml 中“包含”的尽可能少的“正常”xaml。请记住,在合并 xamls 时,它们的顺序非常重要。
  • 我们一开始这样做是因为我们需要更高的维护,并且我们认为将其拆分为多个 xamls 对我们来说更容易处理。最后,我们的内存消耗非常高,性能也很糟糕。因此,我们必须恢复我们在 2 年内创建的所有这些 xamls,并将其恢复到现在的位置。最大的问题是有一个共享或styles.xaml 并将其“包含”在每个其他xaml 中。永远不要这样做。在我的旧posts 上查看有关该问题的一个。
  • 我的知识还是有点模糊,但据我了解,不,你只创建一次资源,但试图找到它们需要更长的时间,因为不同的字典要遍历多次。如果您有 20 个 xaml 并且都以正确的顺序合并到 app.xaml 中,那么一切都应该没问题。将它们合并到一个 xaml 会更好,但在那之后我没有注意到任何巨大的增长。

标签: .net wpf xaml resourcedictionary


【解决方案1】:

我对 MergedDictionaries 有一个大问题,我相信您的问题也是一样的。 我希望我的 ResourceDictionaries 组织得当,这对我来说意味着有单独的 Buttons.xaml、TextBoxes.xaml、Colors.xaml 等。我将它们合并到 Theme.xaml 中,通常所有样式都在一个单独的程序集中(以便我可以轻松切换主题)。我的ApplicationResources如下:

<Application.Resources>
  <ResourceDictionary>
    <ResourceDictionary.MergedDictionaries>
      <ResourceDictionary Source="/DefaultTheme;component/Theme.xaml" />
    </ResourceDictionary.MergedDictionaries>
    <Style TargetType="{x:Type Ellipse}"/>
  </ResourceDictionary>
</Application.Resources>

在主应用程序程序集中定义的 .xamls 中的每个 StaticResource 都可以正常工作,默认样式可以正常工作,这要归功于 dummy Style。 主题内 .xamls 之间的 StaticResources 不起作用。如果我在 Buttons.xaml 中定义一个使用 Colors.xaml 中的 StaticResource 的样式,则会收到有关 StaticResources 和 UnsetValue 的错误。如果我将 Colors.xaml 添加到 Application MergedDictionaries,它会起作用。

解决方案 0

放弃组织。将所有内容放在一个 .xaml 中。我相信这就是 ResourceDictionaries 通常应该使用的方式,因为 MergedDictionaries 存在所有“问题”(对我来说,这将是一场噩梦)。

解决方案 1

将主题内的所有跨 xaml StaticResource 引用更改为 DynamicResource。它可以工作,但要付出代价,因为 DynamicResources 比 StaticResources 更“重”。

解决方案 2

在使用来自另一个 .xaml 的 StaticResources 的每个主题 .xaml 中,将另一个 ResourceDictionary 添加到 MergedDictionaries。这意味着 Buttons.xaml、TextBoxes.xaml 和其他将在其 MergedDictionaries 中包含 Colors.xaml。这将导致 Colors ResourceDictionary 以多个副本存储在内存中。为避免这种情况,您可能需要查看SharedResourceDictionary

解决方案 3

通过不同的 ResourceDictionaries 设置,不同的嵌套我想出了一个理论

如果在上面的同一 .xaml 或此 ResourceDictionary 的 MergedDictionaries 中未找到 StaticResource,则会在 其他顶级 MergedDictionaries 中搜索它。

我宁愿只向 ApplicationResources 添加一个 .xaml,但我通常最终会使用 两个。您不必将 Theme 中的每个 .xaml 添加到 ApplicationResources,只需 - 例如 - Controls.xaml(任何类型的 MergedDictionaries 嵌套,但不允许 Controls.xaml 的字典之间的交叉引用)和 Common.xaml其中包含所有常见的控件资源。如果 Common.xaml 嵌套也是允许的,但没有交叉引用,则不能有单独的 Colors.xaml 和 Brushes.xaml 使用颜色作为静态资源 - 那么您必须将 3 个 .xaml 添加到 Application MergedDictionaries。

现在我总是使用第三种解决方案,但我认为它并不完美,仍然想知道是否有更好的方法。我希望我正确地解释了您所描述的与我相同的问题。

【讨论】:

  • 太棒了,我想你搞定了,只是两个简单的问题,1.你的外部 DefaultTheme 是什么类型的库。类库或 WPF 类库(我使用的是类库)。 2. 你如何构建你的数据模板,它们经常引用来自另一个 xaml 的样式。我不敢相信关于这些问题的文档很少,我找不到其他任何地方有人真正解释了正在发生的事情,所以谢谢。看起来我也会选择选项 3。
  • @JKing 1:(我使用的是 VS2010)当我检查属性时,输出类型是“类库”,但是根据 AssemblyInfo.cs 中存在的 ThemeInfo 属性判断,当我在创建这个项目时,我一定选择了一些与 wpf 相关的选项。 (以前在寻找更好的解决方案时,我尝试了其他的东西:Generic.xaml 应该是特殊的,但它并没有解决我的问题,我一定是尝试了属性和属性 - 无济于事)
  • @JKing 2:我很少将 DataTemplates 放在我的主题中,但你的观点是正确的。您总是需要做的就是将所有资源分成几层。在第一层中没有对其他 .xamls 的 StaticResource 引用,在第二层中只能引用对第一个的引用,依此类推。也许你最终会得到三个或更多。当我说我使用第三种解决方案时,我想我过于简单化了,而实际上我使用的是 0、1 和 3。我的第一层主要有颜色和画笔,也许还有一些非常基本的样式可以作为基础,所有这些都以一个 . xaml(解决方案 0)。 ...
  • @JKing2 ...当我不想弄乱我的组织或不能轻易分成几层时,我偶尔会切换到 DynamicResource(解决方案 2)。我不知道在 Application.xaml 中有很多链接是不鼓励的。我不知道任何指导方针。您的基本问题在这里:一个文件或 MergedDictionaries。我想你可以通过将所有内容放在一个文件中(取决于你拥有多少资源)获得(略微或显着)更好的性能,但对我来说,单独的文件是必须的。 ...
  • @JKing ... 那么:合并到 App 中还是在 Theme 中?当您合并应用程序中的所有内容时,您可以避免大多数问题,但我希望我的主题可重用(由不同的应用程序),因此当我向主题添加新的 .xaml 时,我不必更新每个应用程序。这就是为什么我希望 App 中的字典越少越好,这就是为什么我不认为第三种解决方案是完美的 - 因为我希望能够在 App 中只添加一个字典来让一切正常工作。
【解决方案2】:

我不得不在我们的应用程序中引入主题并面临这些确切的问题。

简短的回答是这样的: 如果资源字典在 App.xaml 中位于它们之前,则它们可以“查看”其他资源字典。如果您尝试在不是 App.xaml 的文件中使用 MergedDictiories,则资源字典将不会“看到”彼此。

对于 Generic.xaml 中的默认资源:您只能将 App.xaml 中定义的资源或 App.xaml 中的合并字典用作 DynamicResource。您可以将 Generic.xaml 中定义的资源用作 StaticResource,但前提是您的样式是在 Generic.xaml 本身中定义的,而不是在 Generic.xaml 内的合并字典中

完整答案I have a detailed post in my blog about this issue

我建议的解决方案: 创建所需的任何 XAML 层次结构,并将文件放在具有 .txaml 扩展名的文件夹中。 我创建了一个简单的小程序(在下面的 GitHub 中提供),它将作为预构建事件运行并将您的 .txaml 文件合并到一个长的 .XAML 文件中。

这允许您根据需要构建资源文件夹和文件,而不受 WPF 的限制。 StaticResource 和设计器将始终工作。 这是您可以在多个文件中拥有 CustomControl 样式的唯一解决方案,而不仅仅是一个长的 Generic.xaml。

这也将解决多个 XAML 文件产生的任何性能问题。

Xaml merging program in GitHub

【讨论】:

    【解决方案3】:

    除了@lisp answer,我还写了tt模板,它从Default.xaml中获取所有文件,找到它们并加入一个文件,我们可以在app.xaml中使用它

    所以我们可以构造文件,有性能,静态资源会工作......

    <#@ template debug="false" hostspecific="true" language="C#" #>
    <#@ assembly name="System.Core" #>
    <#@ assembly name="System.Xml" #>
    <#@ assembly name="System.Xml.Linq" #>
    <#@ import namespace="System.Linq" #>
    <#@ import namespace="System.Xml.Linq" #>
    <#@ import namespace="System.Text" #>
    <#@ import namespace="System.IO" #>
    <#@ import namespace="System.Collections.Generic" #>
    <#@ output extension=".xaml" #>
    
    <#
        IDictionary<string, XNamespace> GetNamespaces(XDocument doc)
        {
            return doc.Root.Attributes()
                        .Where(a => a.IsNamespaceDeclaration)
                        .GroupBy(a => a.Name.Namespace == XNamespace.None ? string.Empty : a.Name.LocalName, a=>XNamespace.Get(a.Value))
                        .ToDictionary(g => g.Key, g => g.First());
        }
    
        XDocument GetFlattenResourceDocument(string path)
        {
            var xFilePath = this.Host.ResolvePath(path);
            var doc = XDocument.Load(xFilePath);
    
            var defaultNs = doc.Root.GetDefaultNamespace();
    
            var mergedDictElement = doc.Root.Elements(defaultNs + "ResourceDictionary.MergedDictionaries").SingleOrDefault();
            if (mergedDictElement == null)
                return doc;
    
            var rootNamespaces = GetNamespaces(doc);
    
            var mergedResourceDictionaries = mergedDictElement.Elements(defaultNs + "ResourceDictionary");
            var addAfterElement = mergedDictElement as XNode;
    
            foreach(var resourceDict in mergedResourceDictionaries)
            {
                var sourcePath = resourceDict.Attribute("Source").Value;
                var flattenDoc = GetFlattenResourceDocument(sourcePath);
    
                var flatNamespaces = GetNamespaces(flattenDoc);
    
                foreach(var key in flatNamespaces.Keys)
                {
                    if(!rootNamespaces.ContainsKey(key))
                    {
                        var curNamespace = flatNamespaces[key];
                        doc.Root.Add(new XAttribute(XNamespace.Xmlns + key, curNamespace.ToString()));
                        rootNamespaces.Add(key, curNamespace);
                    }
                }
    
                var startComment = new XComment($"Merged from file {sourcePath}");
                var endComment = new XComment($"");
    
                var list = new List<XNode>();
                list.Add(startComment);
                list.AddRange(flattenDoc.Root.Elements());
                list.Add(endComment);
                addAfterElement.AddAfterSelf(list);
    
                addAfterElement = endComment;
    
            }
    
            mergedDictElement.Remove();
    
            return doc;
        }
    #>
    <#= GetFlattenResourceDocument("Default.xaml").ToString() #>
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-01-28
      • 1970-01-01
      • 2017-11-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多