I recently asked a question in the WPF forums about loading xaml components from unreferenced assemblies, using Application.LoadComponent() API. This is a useful scenario especially if you are developing a plugin architecture for your application. Zhou Yong from the forums replied with a useful tidbit, which I will document in this blog post.
Application.LoadComponent() is used to create the object tree from a xaml file (or rather a compiled .baml file). This is useful especially if you want to delay the instantiation of a xaml component until runtime. Typically you would have the xaml files inside your own application assembly, in which case the API works without any extra work. You could also keep the xaml files in a custom-control-library, add a project-reference to this library and still use the API. It would work without any problems. The interesting case comes when you don’t have a project-reference available at compile-time. This is when the API doesn’t work. For plugin development it is the most common case.
Fortunately there are couple of different ways in which you can make it work. For the API to work, you need to have the assembly that contains the xaml file, to be loaded in your AppDomain. If you note the previous two cases: one where the xaml file is part of the application-assembly and the other where the xaml file is contained in a referenced assembly, both of them ensure that the assembly will be loaded into the AppDomain when you make the call. Now lets take the case where you don’t have any reference to the assembly at compile time.
The different approaches
Here I will call ”assembly” as the DLL file that contains the xaml.
1] Assembly is in the same directory as the application (EXE)
2] Assembly is in a sub-directory under the application directory
<?xml version="1.0"?> <configuration> <runtime> <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> <probing privatePath="plugins;special_plugins"/> </assemblyBinding> </runtime> </configuration>
3] A possible third approach involves using the Reflection API. Note that here you would not be using Application.LoadComponent() at all. Instead you would make a call to Activator.CreateInstance() for loading the component. You still have to load the assembly into the AppDomain before calling Activator.CreateInstance(). However I understand that this has performance implications (the forum post has more information). In short, the reflection API is too generic, so it has to do a bunch of checks before it can resolve the type. The code will look something like:
Assembly asm = Assembly.LoadFile(@"C:CustomControlLibrary1.dll"); Type type = asm.GetType("CustomControlLibrary1.UserControl1"); UserControl control = Activator.CreateInstance(type) as UserControl;
The only advantage here is that you are not limited to assemblies only under the application directory (or sub-directories). So you could keep them in some other known-directory and load from there.
Loading XAML components from un-referenced assemblies is a fundamental requirement during plugin-development. Although there are three different approaches, I personally prefer approach# 2]. It is versatile (ie. serves the typical plugin development scenario) and also most performant.
[Edit] Make sure you also check out this blog post about a possible issue using Application.LoadComponent(). I ran into this sometime back.