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

时间:2013-06-12 04:57:51

标签: .net wpf xaml resourcedictionary

我有一个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 dictonary,但如果我将其移动/嵌套在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文件中合并字典一部分的资源。我倾向于同意关于我是否应该使用这种方法的评论,但我想弄明白,因为这个问题的最常见解决方案(参见本文顶部的链接)不起作用,也许这个解决方案可以帮助别人。

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

3 个答案:

答案 0 :(得分:26)

我遇到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工作,默认样式的工作归功于虚拟样式。 什么不起作用的是Theme中的.xamls之间的StaticResources 。如果我使用Colors.xaml中的StaticResource定义Buttons.xaml中的Style,我会收到有关StaticResources和UnsetValue的错误。如果我将Colors.xaml添加到Application MergedDictionaries。

解决方案0

放弃组织。将所有内容放在一个.xaml中。我相信,由于MergedDictionaries存在所有“问题”,因此通常应该使用ResourceDictionaries(对我而言,这将是一场噩梦)。

解决方案1 ​​

将主题内的所有cross-xaml StaticResource引用更改为DynamicResource。它的工作原理是有效的,因为DynamicResources比StaticResources“更重”。

解决方案2

在每个使用另一个.xaml中的StaticResources的Theme .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作为StaticResources的单独Colors.xaml和Brushes.xaml - 那么你必须将3 .xamls添加到Application MergedDictionaries。

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

答案 1 :(得分:1)

我必须在我们的应用程序中引入主题并面对这些确切的问题。

简短的回答是: 资源字典可以“查看”其他资源字典,如果它们在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

答案 2 :(得分: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() #>