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

7tofc5zh  于 2022-12-07  发布在  其他
关注(0)|答案(3)|浏览(127)

I have a WPF .net 4.5 application where I am having trouble merging resource dictionaries.
I have the exact same problem as This SO question and This Question but the accepted solution does not work for me.
I have a resource dictionaries declared in my app.xaml as follows (simplified for clarity):

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

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

<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.
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.
Changing the code to the following got rid of the error above, thanks to lisp comment:

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

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

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

q9yhzks0

q9yhzks01#

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:

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

Solution 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).

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

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

Solution 3

By different ResourceDictionaries setup, different nestings I came up with a theory:
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.
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.
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.

hm2xizp9

hm2xizp92#

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

    • 简短的答案是:**如果在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

yqlxgs2m

yqlxgs2m3#

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

相关问题