我有一个WPF 4.0应用程序,它在菜单命令之类的东西中使用了一些自定义的16x16图标。我想(现在)有两组图标,默认的Vista / 7-ish和一些XP-ish。我想要的是让当前的操作系统确定使用哪些图标。
现在,我已经在主题资源词典(即Aero.NormalColor.xaml等)中定义了指向特定PNG资源的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"/>
我的应用中想要显示图标的任何地方都将Image / Icon的source属性设置为这些BitmapImages之一的StaticResource。
<Image Source="{StaticResource IconSave}"/>
这个想法是因为WPF会根据当前的操作系统和主题自动加载一个主题词典,所以只会加载一组BitmapImage资源,而且这些图标会神奇地成为合适的图标。
然而,这不起作用,我在运行时遇到了可怕的“无法找到资源”异常。我的预感是,这是因为主题文件只搜索自定义控件,而Image不是。
Blend 4对这些没有任何问题,但已经定义了其特殊的DesignTimeResources.xaml文件,并在Aero.NormalColor.xaml上进行了合并。 VS2010扼流圈,但它也没有使用像DesignData文件这样的东西,所以我并不感到惊讶。我目前还有一个单独的资源字典文件(MainSkin.xaml),它被合并到Application资源中。引用样式等等在运行时工作正常。
我是在正确的轨道上,只是有一些轻微的错误?我是否需要做一些完全不同的事情才能获得理想的效果,如果是,那又是什么?
答案 0 :(得分:7)
我发现你可以使用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,您还可以在主题更改时使应用程序动态更新(而不是需要重新启动的StaticResource)。
我认为可以编写一个更清晰的ComponentResourceKey实现来隐藏TypeInTargetAssembly(我会给它一个),但至少这可以让你工作。
为了更新,我刚刚对ComponentResourceKey进行了改进,它将查看当前正在执行的程序集,并找到它可以用于TypeInTargetAssembly的第一个UIElement。
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}}"/>
哪个应该证明更清洁。如果您有任何问题,希望有所帮助并告诉我。