2011-08-24 11 views
5

Tôi có một ứng dụng WPF 4.0 sử dụng một số biểu tượng tùy chỉnh 16x16 trong những thứ như lệnh menu và các mục tương tự. Tôi muốn có (bây giờ) hai bộ biểu tượng, các Vista/7-ish mặc định và một số XP-ish. Những gì tôi muốn là có hệ điều hành hiện tại xác định các biểu tượng để sử dụng.Làm cách nào để xác định tài nguyên biểu tượng trên cơ sở chủ đề cho mỗi hệ thống?

Hiện tại, tôi đã có tài nguyên BitmapImage được xác định trong từ điển tài nguyên chủ đề (ví dụ: Aero.NormalColor.xaml, v.v.) trỏ đến tài nguyên PNG cụ thể.

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

Bất kỳ nơi nào trong ứng dụng của tôi muốn hiển thị thuộc tính nguồn hình ảnh/biểu tượng dưới dạng StaticResource cho một trong những BitmapImages này.

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

Ý tưởng là kể từ WPF tải một cuốn từ điển chủ đề tự động dựa trên hệ điều hành hiện tại và chủ đề, chỉ có một tập hợp các nguồn lực BitmapImage sẽ được nạp và các biểu tượng kỳ diệu sẽ là những người thích hợp.

Điều này, tuy nhiên, không hoạt động và tôi nhận được ngoại lệ "không thể tìm thấy tài nguyên" đáng sợ vào thời gian chạy. Linh cảm của tôi là điều này là bởi vì các tập tin chủ đề chỉ được tìm kiếm cho các điều khiển tùy chỉnh, mà hình ảnh không phải là.

Blend 4 không có vấn đề gì với những điều này, nhưng đã xác định tệp DesignTimeResources.xaml đặc biệt của nó bằng cách hợp nhất trên Aero.NormalColor.xaml. VS2010 chokes, nhưng nó cũng không sử dụng những thứ như tập tin DesignData và như vậy, vì vậy tôi không ngạc nhiên. Tôi hiện cũng có một tệp từ điển tài nguyên riêng biệt (MainSkin.xaml) được hợp nhất vào tài nguyên Ứng dụng. Các kiểu tham chiếu và kiểu dáng như vậy hoạt động tốt ở thời gian chạy.

Tôi đang đi đúng hướng và chỉ có điều gì đó hơi sai? Tôi có cần phải làm một cái gì đó hoàn toàn khác nhau để có được hiệu quả mong muốn, và nếu có, cái gì?

Trả lời

5

Tôi nhận thấy rằng bạn có thể làm việc này bằng cách sử dụng ComponentResourceKey. Trong chủ đề của bạn từ điển tài nguyên xác định các nguồn tài nguyên như sau

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

Ở đây local:CustomControl có thể là cửa sổ chính của bạn hoặc một điều khiển tùy chỉnh trong assembly của bạn. Điều thú vị là, nó không thực sự quan trọng miễn là nó tùy chỉnh, để nó đảm bảo rằng bạn buộc nó tải các tài nguyên này.

Bạn cũng sẽ cần phải cập nhật cho bạn AssemblyInfo.cs để đảm bảo rằng ThemeInfo nhìn vào lắp ráp nguồn cho từ điển tài nguyên chủ đề như sau

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

Bây giờ trong XAML của bạn (bất cứ quyền kiểm soát mà bạn thích, doesn' t phải CustomControl), bạn có thể viết như sau để tận dụng các tài nguyên

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

Bằng cách sử dụng DynamicResource bạn cũng có thể làm cho các ứng dụng tự động cập nhật khi thay đổi theme (chứ không phải StaticResource mà sẽ đòi hỏi ar estart).

Tôi nghĩ có thể viết một cách thực hiện sạch hơn của ComponentResourceKey để ẩn đi TypeInTargetAssembly (mà tôi sẽ cho đi) nhưng ít nhất điều này sẽ giúp bạn làm việc.


Để cập nhật, tôi vừa thực hiện một sự cải tiến trên ComponentResourceKey mà sẽ xem xét việc lắp ráp hiện đang thực hiện và tìm ra UIElement đầu tiên nó có thể sử dụng cho 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; 
     } 
    } 

Bây giờ bạn có thể định nghĩa từ điển tài nguyên với điều này

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

và thông tin này trong kiểm soát của bạn như sau

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

nào nên chứng minh rất nhiều bụi. Hy vọng rằng sẽ giúp và cho tôi biết nếu bạn có bất kỳ vấn đề với nó.

+0

Tôi đã phải sử dụng cú pháp URI gói đầy đủ để làm cho nó hoạt động: 'pack: // application: ,,,/Resources/Icons16/Aero/disk.png' Nếu không, tôi nhận được' DirectoryNotFoundException' vì nó tìm kiếm trong C: \ vì lý do nào đó. –