.net - the - resources in resourcedictionary require ax key attribute
El diccionario de recursos fusionados Wpf no se reconoce desde app.xaml (3)
Tengo una aplicación WPF .net 4.5 en la que tengo problemas para combinar diccionarios de recursos.
Tengo exactamente el mismo problema que esta pregunta SO y esta pregunta, pero la solución aceptada no funciona para mí.
Tengo un diccionario de recursos declarado en mi app.xaml de la siguiente manera (simplificado para mayor claridad):
<Application.Resources>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Skin/ResourceLibrary.xaml" />
<ResourceDictionary Source="Skin/Brushes/ColorStyles.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
Problema: la aplicación puede "VER" el dictonario de ColorStyles cuando se enumera en app.xaml, pero si lo muevo / anilo dentro de ResourceLibrary.xaml, la aplicación no "ve" ColorStyles.xaml y los errores sobre recursos estáticos faltantes Aparecer.
Aquí es cómo creo el diccionario ResourceLibrary.xaml (simplificado):
<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>
Motivo del cambio: Mi organización actual de mis diccionarios de recursos es horrible y necesito cambiarla (ya que estoy creando objetos más de una vez). Quería tener un diccionario de recursos en una carpeta "Piel" y luego subcarpetas para organizar los diccionarios de estilo restantes que se fusionarían todos en el archivo ResourceLibrary.xaml que a su vez sería llamado en app.xaml.
Lo que probé: Sí, traté de usar la solución desde el enlace de arriba:
<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>
pero obtengo el siguiente error en la línea de estilo ficticio:
Error 2 Los elementos de propiedad no pueden estar en el medio del contenido de un elemento. Deben ser antes o después del contenido.
Al cambiar el código a lo siguiente, se eliminó el error anterior, gracias al comentario de ceceo:
<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>
pero la Biblioteca de recursos aún no se está llamando.
También traté de cambiar todas las rutas de archivos para empacar URI, pero eso tampoco solucionó el problema.
Traté de mover el resourceLibrary.xaml y los otros diccionarios de recursos en un proyecto de biblioteca de clase diferente (usando la misma estructura de carpetas y archivos que los anteriores). Luego utilicé el siguiente URI pero todavía no puedo acceder a los recursos declarados en el archivo ResourceLibrary.xaml.
<ResourceDictionary Source="pack://application:,,,/FTC.Style;component/ResourceLibrary.xaml" />
Pero nuevamente, si agrego cada diccionario de recursos al archivo App.Xaml, usando el formato UIR anterior, los recursos se pueden usar.
El error desapareció, pero aún no puedo usar los recursos que forman parte del diccionario fusionado en el archivo ResourceLibrary.xaml. Me inclino a estar de acuerdo con el comentario de dowhilefor sobre si debo usar este enfoque o no, pero quiero resolverlo porque la solución más común a este problema (ver enlaces en la parte superior de esta publicación) no está funcionando y tal vez esta solución podría ayudar a alguien más.
Pregunta: ¿Por qué se ignora el archivo ResourceLibrary.xaml?
Además de @lisp answer, he escrito una plantilla tt, que toma todos los archivos de Default.xaml, los encuentra y se une en un archivo, que podemos usar en app.xaml
Así que podemos estructurar archivos, tener rendimiento, recursos estáticos funcionarán ...
<#@ 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() #>
Tengo un gran problema con MergedDictionaries y creo que su problema es el mismo. Quiero que mis ResourceDictionaries estén correctamente organizados, lo que significa para mí que hay, por ejemplo, Buttons.xaml separados, TextBoxes.xaml, Colors.xaml y así sucesivamente. Los fusiono en Theme.xaml, a menudo todos los estilos están en un conjunto separado (para poder cambiar temas fácilmente). Mis ApplicationResources son los siguientes:
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/DefaultTheme;component/Theme.xaml" />
</ResourceDictionary.MergedDictionaries>
<Style TargetType="{x:Type Ellipse}"/>
</ResourceDictionary>
</Application.Resources>
Y cada StaticResource en los .xamls definidos en el trabajo de ensamblaje de la aplicación principal, los estilos predeterminados funcionan gracias al estilo ficticio. Lo que no funciona son los StaticResources entre .xamls dentro de Theme . Si defino un Estilo en Buttons.xaml que usa un StaticResource de Colors.xaml, aparece un error sobre StaticResources y UnsetValue. Funciona si agrego Colors.xaml a Application MergedDictionaries.
Solución 0
Abandonar la organización Pon todo en uno .xaml. Creo que así es como se suponía que generalmente se usaban los ResourceDictionaries debido a todos los ''problemas'' con MergedDictionaries (para mí esto sería una pesadilla).
Solución 1
Cambie todas las referencias de Cross-xaml StaticResource dentro del tema a DynamicResource. Funciona pero tiene un precio ya que DynamicResources es ''más pesado'' que StaticResources.
Solución 2
En cada Theme .xaml que use StaticResources desde otro .xaml, agréguele otro ResourceDictionary a MergedDictionaries. Eso significa que Buttons.xaml, TextBoxes.xaml y otros tendrían Colors.xaml en sus MergedDictionaries. Como resultado, Colors ResourceDictionary se almacenará en la memoria en varias copias. Para evitar eso, es posible que desee buscar en SharedResourceDictionary .
Solución 3
Mediante diferentes configuraciones de ResourceDictionaries, diferentes anidamientos se me ocurrió una teoría :
Si no se encuentra un StaticResource arriba en el mismo .xaml o en los MergedDictionaries de este ResourceDictionary, se busca en otros MergedDictionaries de nivel superior .
Preferiría agregar a ApplicationResources solo un .xaml, pero generalmente termino usando dos . No tiene que agregar a ApplicationResources cada .xaml que tiene en Theme, solo - por ejemplo - Controls.xaml (con cualquier tipo de MergedDictionaries anidando, pero no se permiten referencias cruzadas entre Dictionaries of Controls.xaml) y Common.xaml que contiene todos los recursos comunes de los controles. En caso de anidación Common.xaml también está permitido, pero no hay referencias cruzadas, no puede haber Colors.xaml y Brushes.xaml separados que usen Colors como StaticResources - entonces tendría que tener 3 .xamls agregados a Application MergedDictionaries.
Ahora siempre uso la tercera solución, pero no la considero perfecta y aún me gustaría saber si hay una mejor. Espero haber interpretado correctamente lo que describiste como el mismo problema que el mío.
Tuve que introducir temas en nuestra aplicación y me enfrenté a estos problemas exactos.
La respuesta corta es esta: los diccionarios de recursos pueden "ver" otros diccionarios de recursos si vienen antes que ellos en App.xaml. Si intenta utilizar MergedDictiories en un archivo que no es App.xaml, los diccionarios de recursos no se "verán" entre sí.
Para recursos predeterminados en Generic.xaml: puede usar los recursos definidos en App.xaml o en un diccionario fusionado fuera de App.xaml solo como DynamicResource . Puede usar recursos definidos en Generic.xaml como StaticResource, pero solo si su estilo está definido en Generic.xaml y no en un diccionario fusionado dentro de Generic.xaml
Para la respuesta completa , tengo una publicación detallada en mi blog sobre este tema
Mi solución sugerida: crea la jerarquía XAML que quieras y coloca tus archivos en una carpeta con extensiones .txaml . Creé un pequeño programa simple (provisto a continuación en GitHub) que se ejecutará como un evento previo a la compilación y fusionará sus archivos .txaml en un archivo .XAML largo.
Esto permite estructurar carpetas y archivos de recursos como quiera, sin las limitaciones de WPF. StaticResource y el diseñador funcionarán siempre. Esta es la única solución donde puede tener estilos CustomControl en múltiples archivos, no solo un Generic.xaml largo.
Esto también resolverá cualquier problema de rendimiento que creen múltiples archivos XAML.