2011-08-24 16 views
5

Tengo una aplicación WPF 4.0 que utiliza algunos iconos personalizados de 16x16 en cosas como comandos de menú y similares. Me gustaría tener (por ahora) dos juegos de iconos, los predeterminados Vista/7-ish y algunos XP-ish. Lo que quiero es que el sistema operativo actual determine qué íconos usar.¿Cómo se definen los recursos de icono por tema de sistema?

En este momento, tengo recursos de BitmapImage definidos en diccionarios de recursos de tema (es decir, Aero.NormalColor.xaml, etc.) que apuntan a un recurso PNG específico.

<!-- Aero.NormalColor.xaml --> 
<BitmapImage x:Key="IconSave" UriSource="/MyWPFApp;component/Resources/Icons16/Aero/disk.png"/> 

<!-- Luna.NormalColor.xaml --> 
<BitmapImage x:Key="IconSave" UriSource="/MyWPFApp;component/Resources/Icons16/Luna/disk.png"/> 

cualquier lugar en mi aplicación que quiere mostrar un icono conjuntos de propiedades fuente de la imagen/de Icon como StaticResource a uno de estos BitmapImages.

<Image Source="{StaticResource IconSave}"/> 

La idea es que desde WPF carga un diccionario temático automáticamente en función del sistema operativo y el tema actual, sólo un conjunto de recursos BitmapImage sería cargado y los iconos mágicamente ser los apropiados.

Esto, sin embargo, no funciona, y aparece la temida excepción "no se puede encontrar el recurso" en tiempo de ejecución. Mi presentimiento es que esto se debe a que los archivos de tema solo se buscan para los controles personalizados, imagen que no es.

Blend 4 no tiene ningún problema con estos, pero tiene definió su archivo especial DesignTimeResources.xaml con una fusión en Aero.NormalColor.xaml. VS2010 se ahoga, pero tampoco usa cosas como archivos DesignData y demás, así que no estoy sorprendido. Actualmente tengo también un archivo de diccionario de recursos separado (MainSkin.xaml) que se fusiona con los recursos de la aplicación. Hacer referencia a estilos y cosas similares funciona bien en tiempo de ejecución.

¿Estoy en el camino correcto y tengo algo ligeramente mal? ¿Debo hacer algo completamente diferente para obtener el efecto deseado y, de ser así, qué?

Respuesta

5

Descubrí que puede hacer que esto funcione con ComponentResourceKey. Dentro de su tema de diccionarios de recursos definen los recursos de la siguiente manera

<!-- themes\aero.normalcolor.xaml --> 
<BitmapImage x:Key="{ComponentResourceKey ResourceId=IconSave, TypeInTargetAssembly={x:Type local:CustomControl}}" UriSource="/MyWPFApp;component/Resources/Icons16/Aero/disk.png"/> 

<!-- themes\luna.normalcolor.xaml --> 
<BitmapImage x:Key="{ComponentResourceKey ResourceId=IconSave, TypeInTargetAssembly={x:Type local:CustomControl}}" UriSource="/MyWPFApp;component/Resources/Icons16/Luna/disk.png"/> 

Aquí el local:CustomControl puede ser o bien la ventana principal o un control personalizado dentro de su montaje. Sin embargo, es interesante que, en realidad, no importa siempre que sea personalizado, de modo que se asegure de obligarlo a cargar estos recursos.

También necesitará para modificar sus AssemblyInfo.cs para asegurarse de que ThemeInfo mira al conjunto de fuente de diccionarios de recursos tema con la siguiente

[assembly:ThemeInfo(ResourceDictionaryLocation.SourceAssembly, ResourceDictionaryLocation.SourceAssembly)] 

ahora a su XAML (cualquiera que sea control usted quiere, doesn' t tiene que ser CustomControl) se puede escribir lo siguiente para hacer uso del recurso

<Image Source="{DynamicResource {ComponentResourceKey TypeInTargetAssembly={x:Type local:CustomControl}, ResourceId=IconSave}}"/> 

mediante el uso de DynamicResource también puede hacer que la aplicación se actualiza dinámicamente cuando el tema cambia (en lugar de StaticResource cuales requerirán ar estart).

Creo que probablemente pueda escribir una implementación más limpia de ComponentResourceKey para ocultar el TypeInTargetAssembly (que probaré) pero al menos esto debería hacer que funcione.


Para actualizar, acabo de implementar una mejora en ComponentResourceKey que se verá en la asamblea se está ejecutando actualmente y encontrar el primer UIElement se puede utilizar para el TypeInTargetAssembly.

public class ThemeResourceKey : ComponentResourceKey 
    { 
     public ThemeResourceKey(String resourceId) 
     { 
      ResourceId = resourceId; 
      var assembly = Assembly.GetExecutingAssembly(); 

      var types = assembly.GetTypes().Where(t => typeof (UIElement).IsAssignableFrom(t)); 
      var uiElementType = types.FirstOrDefault(); 
      if(uiElementType == default(Type)) 
       throw new ArgumentException("No custom UIElements defined within this XAML"); 

      TypeInTargetAssembly = uiElementType; 
     } 
    } 

Ahora se puede definir el diccionario de recursos con este

<!-- themes\aero.normalcolor.xaml --> 
<BitmapImage x:Key="{local:ThemeResourceKey IconSave}" UriSource="/MyWPFApp;component/Resources/Icons16/Aero/disk.png"/> 

y referencia esto en sus controles de la siguiente manera

<Image Source="{DynamicResource {local:ThemeResourceKey IconSave}}"/> 

que debería resultar mucho más limpio. Espero que eso ayude y avíseme si tiene algún problema con eso.

+0

Tuve que usar una sintaxis de URI de paquete completo para que funcionase: 'paquete: // aplicación: ,,,/Resources/Icons16/Aero/disk.png' De lo contrario, obtuve una' DirectoryNotFoundException' tal como era buscando en C: \ por alguna razón. –

Cuestiones relacionadas