Today I needed to load some external xaml into a custom control I was working on. During my search for ways to accomplish the task I’ve found some information, but most of the time it was not really useful. That’s why I will try to expose in this post the options you’ve got to do the job and, finally, I will show you how I solved the situation using a converter.

Basically, we’re facing two problems here:

  • How can we load a external xaml?
  • How can we insert this loaded control into the content property of another one using just xaml?
1. How can we load a external xaml?

One of the approaches that I have found was using the GetManifestResourceStream method from the Assembly class. This works fine but it has some drawbacks that I’m going to show you later.

For this to work we need to do the following:

  • Set the Build Action property of the xaml file to Embedded Resource.
  • When passing the name of the resource (our xaml file) we want to find, we must put our assembly namespace as a prefix.
//In this case we're going to get the current assembly.
//We can use the current type to get it:
this.GetType().Assembly.GetManifestResourceStream("AssemblyNamespace.FolderNameIfNeeded.Filename.xaml");
//or we can use this other method:
System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream("AssemblyNamespace.FolderNameIfNeeded.Filename.xaml");

As you can begin to imagine the problem here is that with the previous approach we’re assuming that our resource is on the same assembly we’re working in. What would happen if the resources were into another assembly?

We should load the specific assembly using its full name!

System.Reflection.Assembly.Load("FULLNAMEASSEMBLY").GetManifestResourceStream("AssemblyNamespace.FolderNameIfNeeded.Filename.xaml");

For me, this was not an acceptable solution. I didn’t want to pass the full assembly name and the resource name. It didn’t seem a standard way to proceed. I had the Image control in mind. There you use a relative URI to load the image file and if the file is in another assembly, then we use something like this:

<Image Source="/AssemblyNamespace;component/Folders/Filename.xaml" />

[

In order to fulfill our needs there’s the GetResourceStream method from the Application class.
What we need is:

  • Set the Build Action property of the xaml file to Resource.
  • Use the URI syntax we talked about previously: /AssemblyNamespace;component/Folders/Filename.xaml.

This feels more suitable and it is aligned with the standards we’re used to! ๐Ÿ˜€

Anyway, no matter which approach we use we’ll have to deal with streams in order to read the content of the file and then use a very helpful class: System.Windows.Markup.XamlReader.

Bear in mind that GetManifestResourceStream returns a Stream you can work with but GetResourceStream returns another kind of class: StreamInfo. Fortunately, this class has a property called Stream which returns precisely that, a Stream.

Now, I’m going to show you how to proceed to get the xaml into a string:

string xaml = null;
//getting a stream using a uri
StreamResourceInfo si = Application.GetResourceStream(new Uri(route, UriKind.Relative));
//checking that everything is ok and reading.
if (si != null && si.Stream != null)
{
   using (var sr = new System.IO.StreamReader(si.Stream))
   {
      xaml = sr.ReadToEnd();
   }
}
//loading the text with our XamlReader and convert it to a FrameworkElement.
result = XamlReader.Load(xaml) as FrameworkElement;

As you can see it’s a very straigth forward technique.

2. How can we insert this loaded control into the content property of another one using just xaml?

Let’s say we want to load a external xaml into the content of a Button. Maybe, our first intuition is to load it directly from code and it is ok. We’ve just loaded the xaml and created a FrameworkElement in code so it seems natural to keep going and add a line adding the new generated FrameworkElement to our Button.Content property.

But, wouldn’t it be nice to have a way to do it all in a declarative way? Imagine passing the URI to the Content property in your XAML just as we do with the Image control and its Source property. Well, this can be achieved using a Converter and the Binding functionality.

Oh, wait! How can we use a Converter if we don’t want to bind the Content property to anything and we just want to pass it directly? There’s a little trick here. Look at this piece of code:

Content="{Binding ., Converter={StaticResource xamlConverter}, ConverterParameter=/AssemblyNamespace;component/Folder/Filename.xaml}"

We’re “cheating” and passing the Uri as a converter parameter while keeping a binding to the root datacontext just to enable the use of the converter.

Take a look at the next section in order to see how the Converter works. Take into account that if you want to use a proper binding (say that you have a viewmodel with a property containing the uri) you can avoid the use of the converter parameter. But let’s cut to the cheese and show you the promised code! ๐Ÿ˜€

The promised converter Here you have the code
public class XamlToFrameworkElement : IValueConverter    
    {

        //USE THIS APPROACH WHEN NOT NEEDING A BINDING:
        //Content="{Binding ., Converter={StaticResource xamlConverter}, ConverterParameter=/AssemblyNamespace;component/Folder/Filename.xaml}"

        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            string xaml = null;
            string route = value as string;
            FrameworkElement result=null;
            if (parameter != null)
            {
                route = parameter as string;
            }

            if (String.IsNullOrEmpty(route))
            {
                return null;
            }

            //start reading
            try
            {
                StreamResourceInfo si = Application.GetResourceStream(new Uri(route, UriKind.Relative));
                if (si != null && si.Stream != null)
                {
                    using (var sr = new System.IO.StreamReader(si.Stream))
                    {
                        xaml = sr.ReadToEnd();
                    }
                }

                result = XamlReader.Load(xaml) as FrameworkElement;
            }
            catch (Exception){
                return null; 
            }
           
            return result;
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

I hope you find it useful! You’re very welcome to share your thoughts on this matter.