2011-08-24 35 views
5

我有一个WPF 4.0应用程序,它使用菜单命令之类的东西中的一些自定义16x16图标。我想(现在)有两套图标,默认的Vista/7-ish和一些XP-ish。我想要的是让当前的操作系统决定使用哪些图标。如何在每个系统主题的基础上定义图标资源?

现在,我已经在指向特定PNG资源的主题资源字典(即Aero.NormalColor.xaml等)中定义了BitmapImage资源。

<!-- 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"/> 

港九我的应用程序,要显示一个图标设置图像/图标的源属性作为一个静态资源这些BitmapImages之一。

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

的想法是,既然WPF加载自动根据当前的OS和主题上一个主题字典,只有一组BitmapImage的资源会被加载和图标会神奇地得到相应的。

但是,这不起作用,我在运行时遇到了可怕的“找不到资源”异常。我的直觉是,这是因为主题文件只能搜索自定义控件,而图像不是。

Blend 4对这些没有任何问题,但它已通过在Aero.NormalColor.xaml上定义了它的特殊DesignTimeResources.xaml文件。 VS2010扼流圈,但它也没有使用DesignData等文件,所以我并不感到惊讶。我目前还有一个单独的资源字典文件(MainSkin.xaml),它被合并到应用程序资源中。引用样式等可以在运行时正常工作。

我在正确的轨道上,只是有点不对吗?我是否需要做一些完全不同的事情才能获得理想的效果?如果是,需要做些什么?

回答

5

我发现你可以使用ComponentResourceKey得到这个工作。在你的主题资源字典定义如下

<!-- 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"/> 

这里local:CustomControl可以是你的主窗口或您的组件内的自定义控件的资源。有趣的是,只要它是自定义的,它并不重要,所以它确保你强制它加载这些资源。

您还需要更新您的AssemblyInfo.cs确保ThemeInfo着眼于源组件为主题资源字典有以下

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

现在你的XAML中(控制任何你喜欢的,没有按”吨有是CustomControl),您可以编写以下,使资源

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

的使用使用DynamicResource还可以使应用程序动态更新时主题更改(而不是静态资源,这将需要AR estart)。

我认为可以编写一个更清洁的ComponentResourceKey实现来隐藏TypeInTargetAssembly(我将继续介绍),但至少应该让你工作。


要更新,我刚刚实施了ComponentResourceKey一种改进方法,将着眼于当前执行的程序集,并找到第一的UIElement它可以使用的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; 
     } 
    } 

现在,您可以定义资源字典与此

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

和参考这在你的控制如下

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

这应该证明变得更干净。希望有所帮助,并让我知道你是否有任何问题。

+0

我不得不使用全包URI语法才能使它工作:'pack:// application:,,,, Resources/Icons16/Aero/disk.png'否则,我得到了一个'DirectoryNotFoundException',因为它是看着C:\出于某种原因。 –

相关问题