.net 无法从 app.xaml 识别 Wpf 合并资源字典

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/17057934/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 12:18:23  来源:igfitidea点击:

Wpf merged resource dictionary no being recognized from app.xaml

.netwpfxamlresourcedictionary

提问by J King

I have a WPF .net 4.5 application where I am having trouble merging resource dictionaries.

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

I have the exact same problem as This SO questionand This Questionbut the accepted solution does not work for me.

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

I have a resource dictionaries declared in my app.xaml as follows (simplified for clarity):

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

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

Problem:The app can "SEE" the ColorStyles dictonary when listed in app.xaml, but if I move/nest it inside the ResourceLibrary.xaml, then the ColorStyles.xaml are not "seen" by the app and errors about missing static resources appear.

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

Here is how I create the ResourceLibrary.xaml dictionary (simplified):

这是我创建 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>

Reason for change:My current organization of my resource dictionaries is awful and I need to change it (as I am creating objects more than once). I wanted to have one resource dictionary in a "Skin" folder and then sub-folders for organizing the remaining style dictionaries which would all be merged in the ResourceLibrary.xaml file which in turn would be called in app.xaml.

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

What I tried:Yes I did try to use the solution from the link above:

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

<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>

but I get the following error on the dummy style line:

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

Error 2 Property elements cannot be in the middle of an element's content. They must be before or after the content.

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

Changing the code to the following got rid of the error above, thanks to lisp comment:

由于 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>

but the Resource Library is still not being called.

但是资源库仍然没有被调用。

I also tried to change all the file paths to pack URI's, but that did not solve the problem either.

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

I tried moving the resourceLibrary.xaml and the other resource dictionaries into a different class library project (using the same folder structure and files as above). I then used the following URI but I still am not able to access resources declared in the ResourceLibrary.xaml file.

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

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

But again, if I add each resource dictionary to the App.Xaml file, using the UIR format above, the resources are usable.

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

The error is gone, but I am still unable to use resources that are a part of the merged dictionary in the ResourceLibrary.xaml file. I am inclined to agree with the comment of dowhilefor as to whether or not I should use this approach, but I want to figure this out because the most common solution to this problem (see links at top of this post) is not working and maybe this solution could help someone else.

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

Question:Why is the ResourceLibrary.xaml file being ignored?

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

回答by lisp

I hava a big problem with MergedDictionaries and I believe that your problem is the same. I want my ResourceDictionaries to be properly organized, which means for me that there are for example seperate Buttons.xaml, TextBoxes.xaml, Colors.xaml and so on. I merge them in Theme.xaml, often all the Styles are in a seperate assembly (so that I could easily switch Themes). My ApplicationResources are as follows:

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

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

And every StaticResource in the .xamls defined in the main application assembly work, default styles work thanks to the dummy Style. What doesn't work are StaticResources between .xamls inside of Theme. If I define a Style in Buttons.xaml that uses a StaticResource from Colors.xaml, I get an error about StaticResources and UnsetValue. It works if I add Colors.xaml to Application MergedDictionaries.

并且在主应用程序组装工作中定义的 .xaml 中的每个 StaticResource,由于虚拟样式,默认样式都起作用。 不起作用的是 Theme 内部 .xamls 之间的 StaticResources。如果我在 Buttons.xaml 中定义使用 Colors.xaml 中的 StaticResource 的样式,则会收到关于 StaticResources 和 UnsetValue 的错误。如果我将 Colors.xaml 添加到 Application MergedDictionaries,它会起作用。

Solution 0

解决方案 0

Abandon organization. Put everything in one .xaml. I believe that is how the ResourceDictionaries were generally supposed to be used because of all the 'problems' with MergedDictionaries (for me this would be a nightmare).

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

Solution 1

解决方案1

Change all cross-xaml StaticResource references inside of theme to DynamicResource. It works but comes with a price as DynamicResources are 'heavier' than StaticResources.

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

Solution 2

解决方案2

In every Theme .xaml that uses StaticResources from another .xaml, add this another ResourceDictionary to MergedDictionaries. That means that Buttons.xaml, TextBoxes.xaml and others would have Colors.xaml in their MergedDictionaries. It will result in Colors ResourceDictionary being stored in memory in multiple copies. To avoid that you might want to look into SharedResourceDictionary.

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

Solution 3

解决方案3

By different ResourceDictionaries setup, different nestings I came up with a theory:

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

If a StaticResource isn't found above in the same .xaml or in the MergedDictionaries of this ResourceDictionary, it is searched in other top-level MergedDictionaries.

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

I would prefer to add to ApplicationResources only one .xaml, but I usually end up using two. You dont have to add to ApplicationResources every .xaml that you have in Theme, just - for example - Controls.xaml (with any kind of MergedDictionaries nesting, but no cross-references between Dictionaries of Controls.xaml are allowed) and Common.xaml which contains all common Resources of Controls. In case of Common.xaml nesting is also allowed, but no cross-references, there cannot be seperate Colors.xaml and Brushes.xaml that uses Colors as StaticResources - then you would have to have 3 .xamls added to Application MergedDictionaries.

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

Now I always use the third solution, but I don't consider it perfect and still would like to know if there is a better way. I hope I correctly interpreted what you described as the same problem as mine.

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

回答by Michael_S_

I had to introduce themes into our application and faced these exact problems.

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

The short answer is this:Resource dictionaries can "See" other Resource dictionaries if they come before them in App.xaml. If you try to use MergedDictiories in a file that is not App.xaml, the resource dictionaries will not "See" each other.

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

For default resources in Generic.xaml: You can use resources defined in App.xaml or a merged dictionary out of App.xaml as DynamicResourceonly. You can use resources defined in Generic.xaml as StaticResource, but only if your style is defined in Generic.xaml itself and not in a merged dictionary inside of Generic.xaml

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

For the full answer I have a detailed post in my blog about this issue

对于完整的答案,我在我的博客中有一篇关于这个问题的详细帖子

My suggested solution:Create whatever XAML hierarchy you want, and place your files in a folder with .txamlextensions. I created a small simple program (provided below in GitHub) that will run as a pre-build event and merge your .txaml files into one long .XAML file.

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

This allows to structure resources folders and files however you want, without WPF's limitations. StaticResource and the designer will work always. This is the only solution where you can have CustomControl styles in multiple files, not just one long Generic.xaml.

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

This will also solve whatever performance issues multiple XAML files create.

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

Xaml merging program in GitHub

GitHub 中的 Xaml 合并程序

回答by Arsen Mkrtchyan

In addition to @lisp answer, I have written tt template, which take all files from Default.xaml, find them and join into one file, which than we can use in app.xaml

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

So we can structure files, have performance, static resource will work...

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

<#@ 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() #>