.net - santiago - ¿Cómo forzar a WPF a usar los URI de recursos que usan el nombre fuerte de ensamblado? Argh!
nombres europeos para niña (6)
He experimentado este mismo problema y esta podría ser una posible solución
cada vez que se crea un control utilizando una página .xaml, en el constructor del archivo .cs adjunto, antes de la llamada InitializeComponent (), agregue las siguientes líneas:
contentLoaded = true; var assemblyName = GetType().Assembly.GetName(); System.Windows.Application.LoadComponent(GetType(), new Uri( string.Format("/{0};v{1};component{2}/{3}.xaml", assemblyName.Name, assemblyName.Version, [[[namespace]]], type.Name ), UriKind.Relative))
donde como [[[namespace]]] ingrese el espacio de nombres completo de la clase, excepto el espacio de nombres predeterminado del proyecto de estudio visual
(Nota: hay una marca abierta en conectarse https://connect.microsoft.com/VisualStudio/feedback/details/668914/xaml-generated-code-uses-resource-uri-without-assembly-strong-name )
Ok, esto es realmente irritante, ya me había dado cuenta de que el código generado por WPF para cargar recursos XAML no parecía usar nombres fuertes y, por lo tanto, puede ser problemático para escenarios en los que necesite soportar versiones de ensambles WPF lado a lado.
Este ha sido el caso, y ahora me está causando problemas. Tengo un sistema de complemento que se supone que es compatible con la instalación paralela de complementos que solo difieren en sus números de versión (sus versiones de ensamblaje). Esto por supuesto puede ser soportado por .NET ya que se determina que los ensamblados tienen diferentes identidades incluso si tienen el mismo nombre de archivo DLL, siempre que tengan un nombre fuerte y que tengan una clave pública / privada diferente O tengan un número de versión de ensamblaje diferente.
Ahora, si miramos el código generado para Windows y controles de usuario por Visual Studio, vemos en el archivo autogenerado lo siguiente:
/// <summary>
/// InitializeComponent
/// </summary>
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
public void InitializeComponent() {
if (_contentLoaded) {
return;
}
_contentLoaded = true;
System.Uri resourceLocater = new System.Uri("/Sensormatic.AMK1000.Panel;component/views/servicepanelui.xaml", System.UriKind.Relative);
#line 1 "../../../Views/ServicePanelUI.xaml"
System.Windows.Application.LoadComponent(this, resourceLocater);
#line default
#line hidden
}
Observe la línea donde se crea el localizador de recursos: está utilizando un URI relativo que no especifica el nombre fuerte o la versión del ensamblado que contiene el recurso xaml.
Pensé que quizás LoadComponent verificaría la identidad del ensamblado que llama y usaría su clave pública y detalles de la versión o tal vez verificaría la identidad del ensamblado que contiene el tipo para el parámetro ''this''.
Parece que este no es el caso: si tiene dos ensamblajes con diferentes números de versión (pero el mismo nombre de archivo), puede obtener una IOException con el mensaje "No se puede localizar el recurso X" (para el ejemplo anterior "No se pueden encontrar recursos" views / servicepanelui .xaml ''.
Peor aún, estoy bastante seguro de que esto también significará que los ensamblados con el mismo nombre de archivo pero diferente clave pública / privada, es decir, de diferentes editores, también generarán este error.
Entonces, ¿alguien sabe cómo evitar esto? Cómo hacer que WPF sea compatible con el nombre.
Tenga en cuenta que, en lo que a mí respecta, este es un error de WPF. No debería tener que usar el aislamiento Appdomain solo para evitar esto.
Tiendo a aceptar que esto es probablemente un error, o al menos una deficiencia en las herramientas XAML. Tal vez deberías informarlo en Connect .
No lo he intentado, pero aquí hay un par de posibles soluciones:
- Inyecte un paso previo a la compilación para modificar automáticamente los archivos .g.cs para usar los URI del paquete que especifiquen la información de ensamblaje completa ( AssemblyShortName [; Version] [; PublicKey]; componente / Ruta )
- Adjunte a AppDomain.AssemblyResolve para ayudar al CLR a encontrar el ensamblaje correcto
Puede configurar lo siguiente en su archivo de proyecto para cambiar los URI en el código generado:
<PropertyGroup>
<AssemblyVersion>1.0.0.0</AssemblyVersion>
<AssemblyPublicKeyToken>[YOUR_PUBLIC_KEY_TOKEN]</AssemblyPublicKeyToken>
</PropertyGroup>
He estado lidiando con esto en VS2012. No pude lograr que la solución de Riccardo funcione en este entorno. Esta variante de su código ...
_contentLoaded = true;
var assemblyName = GetType().Assembly.GetName();
Application.LoadComponent(this, new Uri(String.Format("/{0};v{1};component/CustomersFrame.xaml", assemblyName.Name, assemblyName.Version), UriKind.Relative));
... resolvió el problema de ''no se puede localizar el recurso'' pero luego di con el siguiente error un poco más adelante en un elemento secundario: ''No se pudo registrar el objeto nombrado. No se puede registrar el nombre duplicado ''buscar'' en este ámbito. ''
La solución de Aaron Marten funciona para mí. Lo siento, no puedo hacer ningún comentario o voto positivo, pero no tengo el representante.
También puede pasar el parámetro / p: AssemblyVersion = $ version al proceso msbuild si sus compilaciones están automatizadas.
Este código, basado en la respuesta de Riccardo, funcionó para mí en VS2010.
Primero definí un método de cargador al que puedo llamar desde mi constructor XAML.
namespace Utility
{
public class Utility
{
public static void LoadXaml(Object obj)
{
var type = obj.GetType();
var assemblyName = type.Assembly.GetName();
var uristring = string.Format("/{0};v{1};component/{2}.xaml",
assemblyName.Name,
assemblyName.Version,
type.Name);
var uri = new Uri(uristring, UriKind.Relative);
System.Windows.Application.LoadComponent(obj, uri);
}
}
}
Luego en el constructor para cada control XAML, reemplacé InitializeComponent () con:
_contentLoaded = true;
Utility.Utility.LoadXaml(this);
InitializeComponent();
Noté que algunos de mis enlaces RelativeSource dejaron de funcionar, pero pude evitar esto.