tags:

views:

286

answers:

3

I have a WPF MarkupExtension that needs to access resources in the default resource assembly. Extension works fine at runtime, but fails in the designer as the extension can't find the default resources in the startup assembly. In order to load up a ResourceManager I need to know what assembly to load the resources from.

At runtime I can handle this easily by passing in an Assembly at Initialization - that works just fine.

However, at Design Time none of this startup code executes so the designer bombs and fails to load the page. So how can I get one of the following generically (without referencing specific application types):

  • The Application's startup assembly (ie. the WPF EXE)
  • The Current XAML Document the Markup Extension is hosted on
A: 

Assembly.GetEntryAssembly.FullName will give you #1. For your other problem, wouldn't Assembly.GetExecutingAssembly do it?

Paul Betts
That works at runtime, but not at design time since the designer takes over the application. I need to get a ref to the start assembly at design time.The problem is the markup extension is in a separate assembly. Right now I've moved it to the main assembly and reference the assembly that way - that works, but i really need to have the extension in a separate assembly.
Rick Strahl
GetEntryAssembly returns null at design time...
Thomas Levesque
+1  A: 

AFAIK, there's no easy and clean way to do that... I use the following method, which looks for an assembly that has an entry-point (i.e. an executable assembly) and contains a class derived from System.Windows.Application :

    public static Assembly GetEntryAssembly()
    {
        // Should work at runtime
        Assembly asm = Assembly.GetEntryAssembly();

        // Design time
        if (asm == null)
        {
            asm = (
                   from a in AppDomain.CurrentDomain.GetAssemblies()
                   where a.EntryPoint != null
                   && a.GetTypes().Any(t => t.IsSubclassOf(typeof(System.Windows.Application)))
                   select a
                  ).FirstOrDefault();
        }

        return asm;
    }

This code needs to be adjusted for specific needs (for instance, it won't work for a WPF control library)

If you prefer to retrieve the XAML root element, you could find some inspiration in the code of a markup extension I wrote some time ago. It finds the root element by using reflection on private/internal fields.

Thomas Levesque
Ah yes, I had found the Context properties before and dismissed using private reflection. But now that I think about this scenario is primarily for the designer so trust issues should not be a problem.Thanks I think this points me in the right direction.
Rick Strahl
The above code works in the designer with and finding the startup assembly. That still leaves controls in other assemblies but that's fairly minor - markup extension default can handle that.FWIW, private reflection code in your post is useful, but unfortunately it fails in the designer. Apparently the designer runs in partial trust after all and the attempt at private Reflection fails. Not only that it aborts code completely - an exception block doesn't capture it.
Rick Strahl
Strange... for me it doesn't crash the designer. Anyway, it's just a dirty workaround to bind InputBinding commands to ViewModel commands.
Thomas Levesque
Thomas, it looks like in the VS Designer it works. In Blend is where it bombs for me without error - just aborts without even catching in my exception handler.
Rick Strahl
A: 

This is exactly what I'm looking for. However, I'm not using the WPF's System.Windows.Application.

Can you help me with the code if I'm trying to get the assembly of the Windows Application in design-time when the code is on a usercontrol that is on one of the forms in the Windows Application?

I've tried the code below, but it "asm" is null:

System.Reflection.Assembly asm = (from a in AppDomain.CurrentDomain.GetAssemblies() where a.EntryPoint != null && a.GetTypes().Any(t => t.IsSubclassOf(typeof(System.Windows.Forms.Application))) select a ).FirstOrDefault();

David
David: On StackOverflow it is not considered good form to post a question using the "Add Answer" button. When you want to ask a new question, even a modification of an existing question, you should click "Ask Question" to create a new question. In your case you would explain your question and include a link to this question saying something like "I saw the answers to this question but they won't work for me because I don't have a System.Windows.Application ...".
Ray Burns