Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Acessing WPF XAML Resources from non-WPF code

I am currently building an application that consists of several components, each of which is essentially a WPF user control with a little C# code around it for the plugin system to work (using MEF).

The problem I am having is that each component should include an icon and for niceness purposes I defined that as a System.Windows.Media.Brush so I can just use the DrawingBrush exported from Design there. Now I need to access that piece of XAML from non-WPF C# where I currently have the horrible workaround of instantiating the user control and asking it for the resource:

private Brush CachedIcon = null;

public override Brush Icon
{
    get
    {
        if (CachedIcon == null)
        {
            CachedIcon = (Brush)(new BlahControl().TryFindResource("Icon"));
        }
        return CachedIcon;
    }
}

I couldn't find a way to read that resource (which is a .xaml file, and referenced in a ResourceDictionary in the custom control) from a "normal" C# class. Anything belonging to WPF has that nice TryFindResource method but how to do that otherwise? I don't want to have the XAML file with the icon lying around un-embedded.

like image 480
Joey Avatar asked Mar 03 '09 21:03

Joey


2 Answers

In your XAML code make sure the icon resource has the build option set to "Resource", and then reference the resource to make it a xaml static resource

<UserControl.Resources>
    <BitmapImage x:Key="icon1" UriSource="Resources/Icon1.ico" />
</UserControl.Resources>

Then in your .Net 2.0 code you will find the resource in the "{xamlName}.g.resource" stream

Example code that loads all icons from a xaml dll into a dictionary:

using System.IO;
using System.Reflection;
using System.Collections;
using System.Resources;

...

var icons = new Dictionary<String, Bitmap>();
var externalBaml = Assembly.LoadFile(Path.Combine(Environment.CurrentDirectory, "MyXaml.dll"));
Stream resourceStream = externalBaml.GetManifestResourceStream(externalBaml.GetName().Name + ".g.resources");
using (ResourceReader resourceReader = new ResourceReader(resourceStream)) {
    foreach (DictionaryEntry resourceEntry in resourceReader) {
        if (resourceEntry.Key.ToString().ToUpper().EndsWith(".ICO")) {
            icons.Add(resourceEntry.Key.ToString(), Image.FromStream(resourceEntry.Value as Stream) as Bitmap);
        }
    }
}
like image 141
TFD Avatar answered Oct 16 '22 19:10

TFD


My suggestions are:

  • Provide metadata on your control about where the icon can be found. You can do this with your own custom attribute (see example 1 below). This metadata will allow you to load the icon without creating an instance of the control.

  • Since you're using MEF, you can use metadata in your export to achieve the same as above. Details here. See example 2 below.

  • Treat your icon as an ImageSource rather than a Brush. You can use WPF's Image control to show your ImageSource, or you can paint it with an ImageBrush.

  • Use the technique provided by TFD to read the resource with the name specified in the metadata. Unfortunately, WPF does not appear to provide anything like a BamlReader, which would make it much cleaner to load the WPF resource from a non-WPF context.

Example 1:

[Icon("MyIconResourceName")]
public class BlahControl : Control
{
    ...
}

Example 2:

[Export(typeof(IApplicationComponent))]
[ExportMetadata("IconResource", "MyIconResourceName")]
public class BlahControl : Control
{
    ...
}
like image 25
Kent Boogaart Avatar answered Oct 16 '22 21:10

Kent Boogaart