-
Notifications
You must be signed in to change notification settings - Fork 129
WPF XAML resources for modular applications
Note: The sample application Waf Information Manager shows the techniques described in this article.
WPF XAML resources
XAML resources is an important WPF concept to reuse UI elements. Typical elements defined as resources are: Styles, Templates, ValueConverters, Images and Brushes. Reusing these elements can help increasing the maintainability of the software and it reduces the memory usage of the application.
These UI resources can be defined at different levels: Application-level or at a WPF element within the logical tree. The level defines on where the resource can be reused. A resource usage (lookup) at a WPF element searches for the closest element that contains the requested resource. It traverses the logical tree by parent to parent until it reaches the root. When it is not found then it looks at the application-level and at last at the system-level for the resource.
Example: Resource MyRedBrush is used by child element Button.
<Grid>
<Grid.Resources>
<SolidColorBrush x:Key="MyRedBrush" Color="Red"/>
</Grid.Resources>
<Button Content="A red button" Margin="100" Background="{StaticResource MyRedBrush}"/>
</Grid>
Modular application
In a modular application, modules that come with UI elements often define their own XAML resources. Ideally, these resources could be registered in a way so that they are shared by all UI elements within the module but not visible outside the module. Unfortunately, WPF does not support this scenario.
The application-level ResourceDictionary must be used to define these module resources. This approach has following characteristics:
- XAML resources are defined within ResourceDictionaries that resides with their owning modules.
- These ResourceDictionaries are added to the MergedDictionaries of the Application. The assembly containing the Application (App.xaml) class should not reference the modules. So, the modules must register their ResourceDictionaries manually via C# code at startup.
Example: Code extract that registers a module’s ResourceDictionary at application level:
var mergedDictionaries = Application.Current.Resources.MergedDictionaries;
var resourceAssembly = typeof(PresentationService).Assembly;
mergedDictionaries.Add(new ResourceDictionary
{
Source = new Uri("pack://application:,,,/" + resourceAssembly.GetName().Name
+ ";Component/" + "Resources/ConverterResources.xaml")
});
- There is no separation of the resources by module level
- A module could use a resource from another module when it knows the resource key. This breaks the modularization as it creates hidden dependencies.
- A module could define a resource with the same resource key as another module does. In this case the last added ResourceDictionary would win. Such issues are often hard to find.
Avoid
- Do not add the same ResourceDictionaries to multiple MergedDictionaries. Every “add” creates a new instance of all resources. This increases the memory usage.
- Avoid organizing the ResourceDictionaries in a hierarchical structure. Resolving the resources won’t work anymore as expected. See: WPF ResourceDictionary.MergedDictionaries resolve issue