Loading/Using Resource Dictionaries from a WinForms hosted WPF control
Asked Answered
D

3

11

I have a Windows Forms application that needs to host a WPF control at runtime. I have the basic hosting and interaction complete (using an ElementHost control) and everything works fine until I try to do something that requires the WPF control to make use of some custom resource dictionaries that are defined. (The WPF control and all of it's resource dictionaries are all defined in the same WPF Control Library DLL.)

As soon as that happens, I get a bunch of errors that look like this:

System.Windows.ResourceDictionary Warning: 9 : Resource not found; ResourceKey='DocumentHeaderInterestStyle'

I have found one reference (link appears dead due to archiving, this might be the same article that was originally referenced). that talks about this, but it seems like the article is approaching things more from the WPF side, but I don't really want to have to make changes to the WPF control as everything works in a stand-alone WPF application.

If the only way to accomplish this is to make changes on the WPF side, I can get those changes made (I'm not responsible for the WPF control library but the person that is also works for the same company so it's not a problem other than getting his time to make the changes.) but I'm hoping for something I can do on the WinForms side to get this working.

The WPF control library has a resource dictionary file named "Default.xaml" defined in the project with the following properties:

Build Action: Page Copy to Output Directory: Do not copy Custom Tool: MSBuild:Compile

The stand-alone WPF application has the following entry in it's App.xaml file:

    <ResourceDictionary x:Uid="ResourceDictionary_1">
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary x:Uid="ResourceDictionary_2" Source="/SmartClient.Infrastructure;component/Themes\Default.xaml"/>
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>

It seems like the control library should already know how to get its resources. Using the Resources.MergedDictionaries.Add() seems like it should work, but where do I get the instance of the existing dictionary?

Decurved answered 23/2, 2009 at 21:33 Comment(0)
D
4

Assuming you know what resources you need (sounds like you do), you should just be able to "inject" them yourself. Something like:

var wpfControl = new ...;
wpfControl.Resources.Add(...);
elementHost.Child = wpfControl;

In your question you mention that there are existing resource dictionaries in the control library. If so, you can just do this:

var wpfControl = new ...;
wpfControl.Resources.MergedDictionaries.Add(/* instance of existing dictionary */);
elementHost.Child = wpfControl;
Dorise answered 23/2, 2009 at 21:43 Comment(4)
Where would the "key" and "value" come from for the call to Add?Decurved
That makes more sense, but shouldn't the dictionaries already be merged? I've updated my question with some more information on how the WPF side of things looks.Decurved
You are rocked, it worked magically. You really saved my time.Caridadcarie
But where are you suppose to run this code? WPF window's constructor?Distinct
O
2

For loading resource dictionaries that are embedded into the assembly, I've used the following snippet for loading them during the runtime:

//using System.Windows
ResourceDictionary dict = new ResourceDictionary();
dict.Source = new Uri("MyResourceDictionary.xaml", UriKind.Relative);

Application.Current.Resources.MergedDictionaries.Add(dict);

This would also work for loading a dictionary in the executable directory.

Orthography answered 21/7, 2009 at 19:5 Comment(0)
J
1

Supposing you have your styles / templates / resources divided into many files Resources1.xaml and Resources2.xaml you can aggregate them into a single resource dictionary (AllResources.xaml). A resource dictionary can be easily added to a control in control's xaml file. See the example below.

(!) Set Resources1.xaml, Resources2.xaml and AllResources.xaml build actions to Page

Resources1.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <ControlTemplate x:Key="ScrollViewerControlTemplate" TargetType="{x:Type ScrollViewer}">
        ...
    </ControlTemplate>

    <LinearGradientBrush x:Key="VerticalScrollBarBackground" EndPoint="1,0" StartPoint="0,0">
        ...
    </LinearGradientBrush>

    <Style x:Key="StyleA" TargetType="{x:Type ScrollBar}">
        ...
    </Style>

</ResourceDictionary>

Resources2.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">


    <Style x:Key="StyleB" TargetType="{x:Type ScrollBar}">
        ...
    </Style> 

    <Style x:Key="StyleC" TargetType="{x:Type TextBlock}">
        ...
    </Style>

</ResourceDictionary>

AllResources.xaml

Add resource dictionaries sources as relative paths to the AllResources.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
    <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary Source="Resources1.xaml" />
        <ResourceDictionary Source="Resources2.xaml" />
    </ResourceDictionary.MergedDictionaries>
</ResourceDictionary>

Finally in your UserControl

<UserControl.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="pack://application:,,,/projectName;component/PathToTheFileRelativeToProjectRootDirectory/AllResources.xaml
        <converters:StringToUpperCaseConverter x:Key="StringToUpperCaseConverter" />
        <converters:LocalizationEntryToStringCaseConverter x:Key="LocalizationEntryToStringCaseConverter" />
    </ResourceDictionary>
</UserControl.Resources>
Jozef answered 8/9, 2015 at 13:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.