Как определить ресурсы значков для каждой темы системы? - PullRequest
6 голосов
/ 24 августа 2011

У меня есть приложение WPF 4.0, которое использует некоторые пользовательские значки 16x16 в таких вещах, как команды меню и тому подобное. Я хотел бы иметь (на данный момент) два набора значков, стандартные Vista / 7-и и некоторые XP-иш. Я хочу, чтобы текущая ОС определяла, какие значки использовать.

Сейчас у меня есть ресурсы BitmapImage, определенные в словарях ресурсов темы (т.е. Aero.NormalColor.xaml и т. Д.), Которые указывают на определенный ресурс PNG.

<!-- 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 в качестве StaticResource устанавливается равным одному из этих BitmapImages.

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

Идея состоит в том, что, поскольку WPF автоматически загружает словарь тем на основе текущей ОС и темы, будет загружен только один набор ресурсов BitmapImage, и значки будут по волшебству подходящими.

Это, однако, не работает, и я получаю страшное исключение "не могу найти ресурс" во время выполнения. Я догадываюсь, что это связано с тем, что файлы тем ищутся только для пользовательских элементов управления, а это не изображение.

Blend 4 не имеет проблем с ними, но имеет , который определил свой специальный файл DesignTimeResources.xaml со слиянием в Aero.NormalColor.xaml. VS2010 задыхается, но он также не может использовать такие вещи, как файлы DesignData и тому подобное, так что я не удивлен. В настоящее время у меня есть также отдельный файл словаря ресурсов (MainSkin.xaml), который объединен с ресурсами приложения. Ссылки на стили и тому подобные из них отлично работают во время выполнения.

Я на правильном пути и просто что-то не так? Нужно ли делать что-то совершенно другое, чтобы получить желаемый эффект, и если да, то что?

1 Ответ

7 голосов
/ 21 сентября 2011

Я обнаружил, что вы можете заставить это работать, используя 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, которое рассмотриттекущая сборка и найти первый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}}"/>

Что должно быть намного чище.Надеюсь, что это поможет, и дайте мне знать, если у вас есть какие-либо проблемы с этим.

...