Windows Presentation Foundation 4.5 Cookbook
上QQ阅读APP看书,第一时间看更新

Managing logical resources

Logical resources may be of various types, such as brushes, geometries, styles, and templates. Placing all those resources in a single file such as App.xaml hinders maintainability. A better approach would be to separate resources of different types (or based on some other criteria) to their own files. Still, they must be referenced somehow from within a common file such as App.xaml so they are recognized. This recipe shows how to do just that.

Getting ready

Make sure Visual Studio is up and running.

How to do it...

We'll create an application that separates its resources across multiple files for convenience and manageability:

  1. Create a new WPF Application named CH02.ManagingResources.
  2. We want to create a separate file that would hold (for example) brush resources. Right-click on the Project node in Solution explorer and select Add | ResourceDictionary…:
    How to do it...
  3. In the Name box, type Brushes.xaml and click on Add.
  4. A XAML editor is opened with a ResourceDictionary as a root elementVisual studio shows no design surface, because a ResourceDictionary is just a collection of any typed objects, not elements. Let's add one fancy Brush:
        <LinearGradientBrush EndPoint="1,0" x:Key="brush1">
            <GradientStop Color="Yellow" Offset="0" />
            <GradientStop Color="Orange" Offset=".7" />
            <GradientStop Color="DarkRed" Offset="1" />
        </LinearGradientBrush>    
  5. Open MainWindow.xaml. Add an Ellipse within the Grid and set its Fill to be the fancy brush:
            <Ellipse Fill="{StaticResource brush1}" />    

    Notice the designer shows nothing. We want Brushes.xaml to be somehow part of the logical resource search.

  6. Open App.xaml. We need to merge external resource dictionaries into the main application dictionary. Add the following inside the <Application.Resources> tag:
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="Brushes.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>     
  7. Now open MainWindow.xaml. The designer should show the brush used correctly (if it doesn't, build the project to refresh the designer). Running the application should show the following:
    How to do it...

How it works...

A ResourceDictionary can incorporate other resource dictionaries using its MergedDictionaries property (a collection). This means a ResourceDictionary can reference as many resource dictionaries as desired and can have its own resources as well.

The Source property must point to the location of the ResourceDictionary. If that location is within a subfolder, that subfolder must be included. For example, if our Brushes.xaml was under a logical folder named Resources, merging that into App.xaml would look like as follows:

<ResourceDictionary Source="Resources/Brushes.xaml" />

There's more...

This idea can be also used to reference logical resources stored in other referenced assemblies. The Source property would have to be based on the pack URI syntax. Suppose that Brushes.xaml was placed in a class library within a Resources folder. The main application could merge it into another ResourceDictionary as follows:

<ResourceDictionary Source="/MyClassLibrary;component/Resources/Brushes.xaml" />

Duplicated keys

Merging different resource dictionaries may cause an issue: two or more resources with the same keys that originate from different merged dictionaries. This is not an error and does not throw an exception. Instead, the selected object is the one from the last resource dictionary added (which has a resource with that key). Furthermore, if a resource in the current resource dictionary has the same key as the any of the resources in its merged dictionaries – it always wins out. Here's an example:

<ResourceDictionary>
   <SolidColorBrush Color="Blue" x:Key="brush1" />
   <ResourceDictionary.MergedDictionaries>
      <ResourceDictionary Source="Resources/Brushes2.xaml" />
      <ResourceDictionary Source="Resources/Brushes.xaml" />
   </ResourceDictionary.MergedDictionaries>
</ResourceDictionary>     

With this markup, the resource named brush1 is a blue SolidColorBrush because it appears in the ResourceDictionary itself. This "overrides" any resources named brush1 in the merged dictionaries. If this blue brush did not exist, brush1 would be looked up in Brushes.xaml first, as this is the last entry in the merged dictionaries collection.