Dear Dr. WPF,
We are running into some issues around application level resources when we attempt to put WPF task pane UI within Office. The primary issue is that the runtime host is not WPF in these cases which means that Application.Current.Resources is not available. That in turn affects how static resource references are handled in the xaml and how FindResource() works in code.
We’re curious if you have any viable solutions or experience with this type of situation. I’m guessing that in addition to Office/VSTO that Winforms hosted interop would have similar issues.
Thanks,
Brian
Dear Brian,
Managing application-level resources in an interop scenario like yours, where WPF is being hosted within another technology like Win32 or Windows Forms, definitely requires more work on the part of the developer. In this post I’ll describe a number of approaches that you might take and you can decide which is the most appropriate for your scenario.
Brief Intro to Resources and Resource Resolution
For those who may be new to WPF (or who may just want a refresher), this section contains a brief introduction to resources and resource resolution. Feel free to skip ahead if you’re already familiar with these concepts.
Each framework element (which means any descendant of FrameworkElement or FrameworkContentElement) has a Resources collection. This collection is of type ResourceDictionary. The ResourceDictionary type truly represents a CLR Dictionary in which each value (or resource) has a unique key.
The fact that every framework element has its own Resources collection allows resources to be defined at any level in the element tree.
<Grid Margin="20" x:Name="RootGrid">
<Grid.Resources>
<Storyboard x:Key="FadeOutGlassStoryboard" TargetProperty="Opacity">
<DoubleAnimation To="0" Duration="0:0:0.5" />
</Storyboard>
</Grid.Resources>
. . .
<StackPanel>
<StackPanel.Resources>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<ContentPresenter
ContentTemplate="{TemplateBinding ContentTemplate}"
Content="{TemplateBinding Content}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</StackPanel.Resources>
. . .
</StackPanel>
</Grid>
In addition, resources can be defined at the application level within a markup file called an application definition file:
<Application x:Class="WindowsApplication1.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="Window1">
<Application.Resources>
<ResourceDictionary>
<Style x:Key="{x:Type GridSplitter}" TargetType="{x:Type GridSplitter}">
<Setter Property="IsTabStop" Value="False" />
</Style>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Resources\ConverterResourceDictionary.xaml"/>
<ResourceDictionary Source="Resources\LogoDictionary.xaml"/>
<ResourceDictionary Source="Resources\ColorsResourceDictionary.xaml"/>
<ResourceDictionary Source="Resources\IconResourceDictionary.xaml"/>
<ResourceDictionary Source="Resources\SimpleStyles.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
And finally, resources can be defined at the theme level. In addition to the built-in Windows themes, control libraries might define their own theme-level resources.
So there are several levels at which resources may be defined. Now let’s look at how you use (a.k.a., reference) resources.
In markup, a resource reference can be either static or dynamic. There are slightly different rules that apply to each:
- A static reference is applied to a property in XAML by using the {StaticResource xxx} markup extension and a dynamic reference is applied via the {DynamicResource yyy} extension, where xxx and yyy represent the respective resource keys.
- A static reference can be used to set any CLR property, whereas a dynamic reference can only be used to set dependency properties on framework elements or freezables or to set the Value property of Setter objects in styles.
- Static resource references are resolved at parse time (for the most part), whereas dynamic references are resolved at run time. This means a static reference can only be used if the resource has been parsed before the reference, whereas a dynamic reference can be used as a forward reference for a resource that is defined later.
The process of resource resolution begins with the referencing element. First, the Resources collection of the referencing element is checked. If a dynamic reference was used and the element has a Template and/or Style, the Resources collections of the Template and Style are also checked. If the resource was not found, resource resolution then continues up the element tree by searching the Resources collections of each parent until the resource is found.
Resource resolution always favors the logical tree. (If an element does not have a logical parent, resolution will begin through the visual tree until an ancestor is found in the logical tree and it will then continue up the logical tree.) If the root of the logical tree is reached and the resource has not been resolved, the application-level resource dictionary will be searched. If still unresolved and a dynamic resource reference was used (or a static reference was deferred), then the theme resources will be checked.
Resources can also be retrieved in code by using the FindResource() method on a framework element. The resolution process is very much the same as with dynamic references.
Resource Considerations for Custom Controls
There are special considerations around resources when writing custom control libraries. My focus in this post is purely on managing application-level resources in a hosted interop scenario. For anyone writing a control library, I would recommend that you check out the following post in the WPF SDK blog:
Using Shared Resources in a Controls Library.
Application-Level Resources in a WPF Application
When building a typical WPF application, the MSBuild project file contains a project element named ApplicationDefinition. This element identifies a XAML file whose root descends from the Application class. So for a C# project, the Application class files would appear as follows in the .csproj file.
<ApplicationDefinition Include=”App.xaml” />
<Compile Include=”App.xaml.cs”>
<DependentUpon>App.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
MSBuild now knows that this class represents the entry point for the WPF application, so it auto-generates a file named App.g.cs that contains an entry point, as follows:
[System.STAThreadAttribute()]
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
public static void Main() {
WindowsApplication1.App app = new WindowsApplication1.App();
app.InitializeComponent();
app.Run();
}
This is the code that instantiates the Application class in a typical WPF application.
As mentioned earlier, the Application class contains a Resources property which identifies the ResourceDictionary where application-level resources reside. A typical ApplicationDefinition file was presented in the introduction section above.
Note that there can be individual resources in the Application’s Resources collection as well as additional merged resource dictionaries. All such resources are available at the application level, and thus, can be referenced freely throughout the application. This provides an elegant mechanism for theming the application, as well as for sharing common resources among different pages, windows, user controls, etc.
Providing Application-Level Resources when WPF is Hosted
When WPF is hosted inside a managed application framework like Windows Forms or in an unmanaged framework like MFC or pure Win32, this elegance provided by application-level resources is lost because WPF’s Application object does not even get created. (Recall that it is the auto-generated code that actually creates the Application instance.) As such, resource resolution occurs only within the element tree and at the theme level.
The good news is that there are several ways to effectively bring back the benefits of application-level resources in these hosted interop scenarios…
Create an Application Instance and Add Resources in Code
The most obvious approach is to simply create an instance of the Application class. Since the Application class is a singleton, you can simply instantiate an instance of the class and then you have access to it throughout your app. Of course, at that point, its Resources collection is empty, so you will probably want to add some resources. Since it is more convenient to declare resources in markup, I typically define my resources in a ResourceDictionary and then merge them into the Resources collection in my Application object.
Below is a very simple function that will create the Application object if it does not exist and then load some resources:
public static void EnsureApplicationResources()
{
if (Application.Current == null)
{
// create the Application object
new Application();
// merge in your application resources
Application.Current.Resources.MergedDictionaries.Add(
Application.LoadComponent(
new Uri("MyLibrary;component/Resources/MyResourceDictionary.xaml",
UriKind.Relative)) as ResourceDictionary);
}
}
Now you just need to make sure that you call this function prior to parsing any XAML files that contain static resource references to application-level resources. To do this, simply add a call to the above function in the constructor of your markup-based classes before any call to InitializeComponent():
public Page1()
{
EnsureApplicationResources();
InitializeComponent();
}
Voîla! You now have application-level resource resolution in your hosted scenario.
There are a couple of things worth pointing out here. First, in this scenario, we are only using the Application object for resource resolution. You would not want to call the Application’s Run() method in the hosted scenario, since that would start a WPF dispatcher. In our scenario, the hosting technology is responsible for dispatching Windows messages. (The exception might be if the host was a Win32 console app, in which case you would need to supply a message pump for WPF.)
Another thing to keep in mind is that the Application singleton will remain alive for the remainder of the process lifetime unless you take steps to release it. If your scenario only calls for WPF to be used for a finite period of time during application execution, you should call the Application’s Shutdown() method when you no longer need it. In a non-hosted WPF application, this would actually shut down the application by shutting down the dispatcher. In our hosted scenario, it will simply release the rooted reference to the singleton. After calling Shutdown(), the static Application.Current property will once again return null. At this point, the object should be available for garbage collection.
Define the Application Class in XAML and Create It on the Fly
The above approach is nifty, but it requires us to do the work of adding and merging resources in code. This is great if you want dynamic control over exactly which resources are added. But if your scenario always calls for the same application-level resources, wouldn’t it be easier to just define those in markup the same way you would for a native WPF application?
Good news… You can! You just need to make a couple of small changes to the MSBuild project file and the code-behind for your Application class.
First, we do not want MSBuild to generate an application entry point for our Application class. So instead of declaring the App.xaml file as an ApplicationDefinition element in the project file, we need to declare it as a Page element:
<Page Include="App.xaml" />
<Compile Include="App.xaml.cs">
<DependentUpon>App.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
Next, we need to make sure that our App.xaml markup is parsed. Typically, this is done as part of the entry point function (which we just eliminated). Instead, we can simply define a constructor for the Application class and call InitializeComponent directly:
public App()
{
InitializeComponent();
}
Now all our resources and merged dictionaries can be declared in App.xaml and our static function to load the Application instance can be as simple as this:
public static void EnsureApplicationResources()
{
if (Application.Current == null)
{
// create the Application object
new App();
}
}
Manage a Collection of Resource Dictionaries in Code and Merge them at the Element Level
The final approach I’ll discuss for managing application resources in a hosted scenario involves programmatically managing a collection of resource dictionaries. In this scenario, we do not leverage an Application object at all. Instead, we dynamically load each ResourceDictionary at runtime and selectively merge it into pages or windows or specific elements, as necessary.
Clearly, this puts more responsibility on the developer for ensuring that the proper resources are merged at the proper locations. But it has benefits too, such as better scoping of resources and not having to track the lifetime of the Application object.
When using this approach, the trick to ensuring that application resources are truly shared across element trees is to only create a single instance of each resource dictionary. This can easily be accommodated by leveraging a static dictionary of dictionaries:
private static Dictionary<string, ResourceDictionary>
_resourceDictionaries = new Dictionary<string, ResourceDictionary>();
Now each dictionary can be referred to using a simple name, such as “ApplicationBrushes”. A static utility function can be used to retrieve the resource dictionary by name. If the dictionary is not loaded, the function can automatically load it:
public static ResourceDictionary GetResourceDictionary(string dictionaryName)
{
if (!_resourceDictionaries.ContainsKey(dictionaryName))
{
_resourceDictionaries[dictionaryName] = Application.LoadComponent(
new Uri("MyLibrary;component/Resources/" + dictionaryName + ".xaml",
UriKind.Relative)) as ResourceDictionary;
}
return _resourceDictionaries[dictionaryName];
}
And to make this solution play nicely in the WPF world, we should allow dictionaries to be merged declaratively in our markup files. One way to do this is to leverage an attached property. Suppose we define a static class called SharedResources and in that class we define a string property called MergedDictionaries. This would allow us to merge in shared resource dictionaries to the Resources collection of any framework element, by simply doing this:
<Grid dw:SharedResources.MergedDictionaries="ApplicationBrushes;ButtonStyles">
. . .
</Grid>
As you can see, my MergedDictionaries property allows for a semicolon-delimited list of resource dictionaries that should be merged into the Resources collection of the Grid.
The only thing left to do now is actually implement the SharedResources class and the MergedDictionaries attached property. Here is that class in its entirety with a few bonus refinements:
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Reflection;
namespace DrWPF.Windows
{
public static class SharedResources
{
#region MergedDictionaries
public static readonly DependencyProperty MergedDictionariesProperty =
DependencyProperty.RegisterAttached("MergedDictionaries",
typeof(string), typeof(SharedResources),
new FrameworkPropertyMetadata((string)null,
new PropertyChangedCallback(OnMergedDictionariesChanged)));
public static string GetMergedDictionaries(DependencyObject d)
{
return (string)d.GetValue(MergedDictionariesProperty);
}
public static void SetMergedDictionaries(DependencyObject d, string value)
{
d.SetValue(MergedDictionariesProperty, value);
}
private static void OnMergedDictionariesChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
if (!string.IsNullOrEmpty(e.NewValue as string))
{
foreach (string dictionaryName in (e.NewValue as string).Split(';'))
{
ResourceDictionary dictionary =
GetResourceDictionary(dictionaryName);
if (dictionary != null)
{
if (d is FrameworkElement)
{
(d as FrameworkElement).Resources
.MergedDictionaries.Add(dictionary);
}
else if (d is FrameworkContentElement)
{
(d as FrameworkContentElement).Resources
.MergedDictionaries.Add(dictionary);
}
}
}
}
}
#endregion
private static ResourceDictionary GetResourceDictionary(string dictionaryName)
{
ResourceDictionary result = null;
if (_sharedDictionaries.ContainsKey(dictionaryName))
{
result = _sharedDictionaries[dictionaryName].Target;
}
if (result == null)
{
string assemblyName = System.IO.Path.GetFileNameWithoutExtension(
Assembly.GetExecutingAssembly().ManifestModule.Name);
result = Application.LoadComponent(new Uri(assemblyName
+ ";component/Resources/" + dictionaryName + ".xaml",
UriKind.Relative)) as ResourceDictionary;
_sharedDictionaries[dictionaryName] = new WeakReference(result);
}
return result;
}
private static Dictionary<string, WeakReference> _sharedDictionaries
= new Dictionary<string, WeakReference>();
}
}
Notice that in this implementation, instead of keeping a dictionary of dictionaries, I’m keeping a dictionary of weak references to dictionaries. This ensures that my static class does not keep any resource dictionary in memory when it is no longer in use. (The class could certainly be augmented to provide a mechanism to lock a resource dictionary in memory for times when you want to persist resources across application lifetime. In the name of simplicity, I’ll leave that as an exercise for the reader.)
So there you have a few options for dealing with application-level resources while running hosted. Hope it helps!
Best regards,
Dr. WPF