我有一个WPF .net 4.5应用程序,我无法合并资源词典。
我遇到了与This SO question和This 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文件?
答案 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。
放弃组织。将所有内容放在一个.xaml中。我相信,由于MergedDictionaries存在所有“问题”,因此通常应该使用ResourceDictionaries(对我而言,这将是一场噩梦)。
将主题内的所有cross-xaml StaticResource引用更改为DynamicResource。它的工作原理是有效的,因为DynamicResources比StaticResources“更重”。
在每个使用另一个.xaml中的StaticResources的Theme .xaml中,将另一个ResourceDictionary添加到MergedDictionaries。这意味着Buttons.xaml,TextBoxes.xaml和其他人在MergedDictionaries中会有Colors.xaml。它将导致Colors ResourceDictionary以多个副本存储在内存中。为避免这种情况,您可能需要查看SharedResourceDictionary。
通过不同的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文件创建的任何性能问题。
答案 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() #>