2009-09-21 18 views
18

O.k, esto es realmente irritante, había notado previamente 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 apoyar versiones de WPF codo a codo.Cómo forzar a WPF a usar los URI de recursos que usan el nombre fuerte de ensamblado? Argh!

Esto ha sido el caso, y ahora me está causando problemas. Tengo un sistema de complemento que se supone que admite 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 tengan una clave pública/privada diferente O tengan un número de versión de ensamblaje diferente.

Ahora bien, si nos fijamos en el código generado para las ventanas y controles de usuario de Visual Studio, que vemos en el archivo generado automáticamente 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 
} 

Aviso de la línea donde se crea el localizador de recursos - es 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 no es el caso: si tiene dos ensamblajes con números de versión diferentes (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 puede localizar el recurso 'views/servicepanelui.xaml'.

Peor, 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 darán como resultado este error

Entonces, ¿alguien sabe cómo evitar esto? Cómo hacer que WPF sea compatible con el nombre

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

+0

Hola Phil, estoy enfrentando el mismo problema. ¿Pudiste encontrar alguna solución a esto? –

+0

¿Alguien ha enfrentado un problema similar con los controles personalizados de WPF que utilizan temas (a través de diccionarios de recursos)? Si es así, ¿alguna solución para eso? – akjoshi

+0

alguna noticia sobre esto? –

Respuesta

3

Tiendo a aceptar que esto es probablemente un error, o al menos una deficiencia en las herramientas XAML. Quizás deba informarlo en Connect.

no lo han intentado, pero aquí hay un par de soluciones posibles:

  1. Inyectar una etapa de pre-construcción para modificar automáticamente los archivos .g.cs utilizar pack URIs que especifican la información de ensamblado completo (AssemblyShortName [; Version] [; PublicKey]; componente/Ruta)
  2. Unir al AppDomain.AssemblyResolve para ayudar al CLR encontrar el ensamblaje derecho
+0

Gracias Kent, ¡aunque esperaba que hubiera una forma incorporada de manejar esto! (aunque no lo he encontrado). No creo que el # 2 vaya a ayudar, bastante seguro de que la solicitud de resolución de ensamblaje solo tendrá el nombre débil ya que eso es lo que especifica el URI, y entonces no sé cuál es el ensamblado que debería ser usado. # 1 podría funcionar, aunque obviamente es feo. Por cierto, buen trabajo en truss. Utilizándolo en mi proyecto actual. – Phil

+0

adjuntándose a una resolución de montaje, no tener una pista de cuál desea cargar no me parece útil, tal vez puede usar el conjunto solicitante si está especificado –

3

que han experimentado este mismo probl em y esta podría ser una posible solución

cada vez que se crea un control utilizando una página .xaml, en el adjunto.cs archivo constructor, antes de la llamada InitializeComponent(), añadir 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 [[[espacio de nombres]]] entrar en el espacio de nombre completo de la clase, excepto el espacio de nombres por defecto del proyecto de Visual Studio

(Nota: hay un proceso abierto un tictac al conectar https://connect.microsoft.com/VisualStudio/feedback/details/668914/xaml-generated-code-uses-resource-uri-without-assembly-strong-name)

+0

en [[[espacio de nombres]]] no es el espacio de nombres sino el ubicación relativa al proyecto de la página que está intentando cargar (que generalmente coincide con el espacio de nombres sin la raíz) –

5

puede establecer lo siguiente en el archivo de proyecto para cambiar el URI de en el código generado:

<PropertyGroup> 
    <AssemblyVersion>1.0.0.0</AssemblyVersion> 
    <AssemblyPublicKeyToken>[YOUR_PUBLIC_KEY_TOKEN]</AssemblyPublicKeyToken> 
</PropertyGroup> 
+1

¿Cómo lo supo? Esto es tan ... bueno esotérico. esto no es, incluso, oficialmente respaldado, ¡pero funciona! – user195275

+0

no me gusta esta solución, ¿qué sucede si cambio la versión (sin refactor automático) o si tengo una clave diferente en mi máquina de depuración y en el servidor de compilación? –

+0

¡Estaba trabajando en este tema por más de una semana y estaba sufriendo mucho! ¡Tu respuesta funcionó para mí como un amuleto y me ayudó mucho! ¡¡Gracias!! (también tenga en cuenta [este sitio] (https: //alexfeinberg.wordpress.com/2014/11/08/microsoft-mechanism-to-load-wpf-resources-is-broken-looking-for-a-better-title /), que citó este hilo) –

1

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)); 

... resuelve el problema 'no puede localizar los recursos' pero luego me golpeó el siguiente error un poco más a lo largo de un elemento hijo: 'No se pudo registrar 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.

1

También puede pasar el parámetro/p: AssemblyVersion = $ version al proceso msbuild si sus compilaciones están automatizadas.

+0

Estaba trabajando en este tema ahora para ¡más de una semana y estaba sufriendo mucho! ¡Tu respuesta funcionó para mí como un amuleto y me ayudó mucho! ¡¡Gracias!! (también tenga en cuenta [este sitio] (https://alexfeinberg.wordpress.com/2014/11/08/microsoft-mechanism-to-load-wpf-resources-is-broken-looking-for-a-better-title/), que citó tu respuesta) –

+0

Ubiqué mi blog – user195275

0

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 de 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); 
     } 
    } 
} 

A continuación, en el constructor para cada control XAML, que sustituyen InitializeComponent() con:

 _contentLoaded = true; 
     Utility.Utility.LoadXaml(this); 
     InitializeComponent(); 

me di cuenta de que algunos de mis fijaciones RelativeSource dejó de funcionar, pero yo era capaz de solucionar este .

Cuestiones relacionadas