Wpf合并的资源字典在app.xaml中未被识别

26
我有一个WPF .net 4.5应用程序,我在合并资源字典方面遇到了麻烦。
我和这个SO问题以及这个问题遇到了完全相同的问题,但是被接受的解决方案对我无效。
我在我的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" />

但是,如果我将每个资源字典按照上述UIR格式添加到App.Xaml文件中,则这些资源是可用的。

错误已经消失了,但我仍然无法使用ResourceLibrary.xaml文件中包含的合并字典的资源。我倾向于同意dowhilefor的评论,即我是否应该使用此方法,但我想找出原因,因为这个问题的最常见解决方案(请参见本帖子顶部的链接)不起作用,也许这个解决方案可以帮助其他人。

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


1
你的虚拟样式相关的xaml与发布的完全一样吗?我没有“错误2”,你可能在原始代码中有语法错误,请参见此处 - lisp
3
我可以给你一个好的建议:尽可能使用较少的XAML文件。不要试图通过分割来过度组织它。我们使用了500个XAML文件,我仍然在夜里哭泣和出汗。你应该最多只有3个层。app.xaml包含所有资源字典,libs中的Generic.xaml包含默认控件,当然还有尽可能少的“普通”XAML文件,这些文件被“包含”在app.xaml中。记住,在合并XAML文件时,它们的顺序非常重要。 - dowhilefor
@dowhilefor的缺点主要是维护问题还是由于太多嵌套的XAML字典而导致性能问题? - J King
1
我们最初这样做是因为我们想要更高的维护性,并且认为将其分成多个XAML文件更容易处理。最终,我们的内存消耗非常高,性能非常差。因此,我们不得不恢复所有这些在两年内创建的XAML文件,并将其带回到现在的状态。最大的问题是拥有一个共享的styles.xaml并在每个其他xaml中“包含”它。不要,永远不要这样做。可以查看我旧文章中关于该问题的链接 - dowhilefor
1
我的知识还有些模糊,但从我的理解来看,你只需要创建资源一次,但是尝试查找它们需要更长的时间,因为不同的字典被多次遍历。如果你有20个xaml文件,并且按正确顺序合并到app.xaml中,那么一切都应该没问题。将它们合并成一个xaml文件会更好,但我没有注意到明显的增加。 - dowhilefor
显示剩余5条评论
3个回答

30

我在使用MergedDictionaries时遇到了一个大问题,我相信你的问题也是一样的。 我希望我的ResourceDictionaries能够得到良好的组织,这对我来说意味着有独立的Buttons.xaml、TextBoxes.xaml、Colors.xaml等等。我将它们合并在Theme.xaml中,通常所有的Styles都在一个单独的程序集中(这样我就可以轻松地切换主题)。我的ApplicationResources如下:

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

主应用程序集中定义的每个.xaml中的StaticResource都可以工作,虚拟样式使默认样式工作。

不起作用的是Theme内部.xamls之间的StaticResources。如果我在Buttons.xaml中定义使用Colors.xaml的StaticResource的样式,则会出现有关StaticResources和UnsetValue的错误。如果我将Colors.xaml添加到Application MergedDictionaries,它就可以正常工作。

解决方案0

放弃组织。将所有内容放在一个.xaml文件中。我认为这通常是ResourceDictionaries的使用方式,因为合并字典(对我来说)可能会是一场噩梦。

解决方案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中搜索。

我更喜欢只添加一个.xaml到ApplicationResources,但通常最终使用两个。您不必将每个.xaml都添加到ApplicationResources中,只需例如Controls.xaml(具有任何类型的MergedDictionaries嵌套,但不允许交叉引用Controls.xaml字典之间)和包含所有控件的公共资源的Common.xaml即可。在Common.xaml的情况下,也允许嵌套,但不允许交叉引用,不能有单独使用Colors作为StaticResources的Brushes.xaml和Colors.xaml,然后您将必须有3个.xaml添加到Application MergedDictionaries中。

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


2
@JKing 1:(我正在使用VS2010)当我检查属性时,输出类型为“类库”,但根据AssemblyInfo.cs中存在的ThemeInfo属性来判断,创建此项目时我必须选择了一些与wpf相关的选项。(当我寻找更好的解决方案时,我尝试了其他方法:Generic.xaml应该是特殊的,但它没有解决我的问题,我必须尝试属性和属性 - 但都无济于事) - lisp
最后一个问题,你知道为什么不建议从application.xaml中有很多链接到资源字典吗?将它们合并成2-3个字典,然后从application.xaml引用它们是否有真正的好处? - J King
2
@JKing 2:我很少把DataTemplates放在我的主题中,但你的观点是正确的。你必须将所有资源分成层次结构。第一层没有对其他.xaml文件的StaticResource引用,第二层只能引用第一层,以此类推。也许你最后会有三个或更多层次。当我说我使用第三种解决方案时,我可能过于简化了,事实上我使用0、1和3。我的第一层主要包括颜色、画刷和一些非常基本的样式,并且所有这些都在一个.xaml文件中(解决方案0)。... - lisp
2
@JKing2...当我不想弄乱我的组织或不能轻松地分成层时,我偶尔会切换到DynamicResource(解决方案2)。我不知道在Application.xaml中有很多链接是不被鼓励的。我不知道有什么指导方针。你的基本问题在于:一个文件还是MergedDictionaries。我猜如果把所有东西都放在一个文件中(取决于你有多少资源),你会得到(稍微或显著)更好的性能,但对我来说,单独的文件是必须的。... - lisp
2
@JKing ... 然后:在应用程序中合并还是在主题中合并?当您将所有内容合并到应用程序中时,可以避免大多数问题,但我希望我的主题可重用(由不同的应用程序使用),因此当我向主题添加新的.xaml时,我不必更新每个应用程序。这就是为什么我希望在应用程序中尽可能少地使用字典,这就是为什么我不认为第三种解决方案是完美的原因 - 因为我希望能够仅添加一个字典以使一切正常运行。 - lisp
显示剩余4条评论

2

我不得不在我们的应用程序中引入主题,并遇到了这些确切的问题。

如果Resource dictionaries在App.xaml之前,那么它们可以“看到”其他Resource dictionaries。如果您尝试在不是App.xaml的文件中使用MergedDictiories,则资源字典将无法“看到”彼此。

对于Generic.xaml中的默认资源:您只能使用从App.xaml定义的资源或合并的字典作为DynamicResource。您可以使用在Generic.xaml中定义的资源作为StaticResource,但仅当您的样式在Generic.xaml本身中定义而不是在Generic.xaml内部的合并字典中定义时才能使用。

有关完整答案,请参见我的博客上关于此问题的详细帖子

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

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

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

GitHub中的Xaml合并程序


0
除了@lisp的回答之外,我还编写了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() #>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接