Assembly-level initialization at design time

One sorely missing feature from the Blend 4 Beta is the ability to have a method in which you can perform initialization work specific to design-time.  Such a method is useful for tasks like loading design-time data services into a container, configuring MEF with design-time specific dependencies, and basically anything else that you might otherwise do at startup when the application runs.  Since your App class won’t be started up when in design mode, it would be great if there was a way for Blend to call a method designated for that purpose at design-time.

I have been thinking about this a lot recently.  Initially I thought that Blend could provide an attribute that I could apply to a static method, and it would then invoke that decorated method before loading any Views.  That seemed like the best solution (and still does, in my opinion).  However, since Blend has no such attribute, I figured it was a moot point…until I realized that I could create my own attribute for the same purpose!

If you decorate an assembly with an attribute, that attribute must be instantiated when the assembly is inspected via reflection (which Blend most certainly does).  So, I simply created a custom attribute and applied it to my assembly.  In that attribute’s constructor, I check to see if it was loaded into design-time.  If it was, I then perform my initialization logic.  How simple!

Here’s the code I added to AssemblyInfo.cs in a project loaded by Blend 4 Beta:

I assume people might want to genericize this class a bit, but the basic idea is quite simple.  Perhaps adding a Type parameter for a class to instantiate would be better, so that the instantiated class could handle initialization.  That would leave the attribute very simple and reusable.  I’ll leave that up to you, if you care…

Here’s the code, for copy-and-paste purposes:

// In AssemblyInfo.cs
//
// Apply this attribute to the assembly so
// that it will be created when the assembly
// is loaded into memory.
[assembly: DesignTimeBootstrapper]

[AttributeUsage(AttributeTargets.Assembly)]
class DesignTimeBootstrapperAttribute
: Attribute
{
public DesignTimeBootstrapperAttribute()
{
var dep = new DependencyObject();
if (DesignerProperties.GetIsInDesignMode(dep))
{
// TODO: Design-time initialization…
}
}
}

Happy coding!

About these ads

15 Responses to Assembly-level initialization at design time

  1. This is a great post.

    One suggestion, use the static constructor instead of the instance constructor to call bootstrapper code.

    I’ve found that VS does repeated assembly loads. This could be happening because it is reflecting over the assembly types, not sure.

    Awesome,

    kdawg

  2. Josh Smith says:

    Thanks for pointing out that glitch in VS, Karl. I wonder why VS/Cider is doing that…

    Using the attribute’s static constructor works fine. I guess that’s a better approach.

  3. Maybe they are reflecting the assembly, could that cause this? BTW: I did my testing on VS 2008.

    Also, readers should know, this attribute only gets called when the a XAML file is opened in the XAML Editor or Designer. This attribute would not get called when a code file is loaded up.

    JJ, this simplifies many design-time scenarios and gets design-time goo out of our ViewModels. Yea!

    Cheers,

    kdawg

  4. Josh Smith says:

    I’m not sure what you mean. The attribute is created when the assembly is loaded into Blend, which happens before any XAML or code file is opened.

    Josh

  5. I was doing my tracking after rebuilds in VS 2008.

    Since the attribute code only runs when in a designer, the code

    // TODO: Design-time initialization…

    only runs when a XAML Editor or Designer is opened.

    In VS 2008, when I opened the designer, it runs 3 times.

    So by putting the code to run in the static constructor, the GetIsInDesignMode code is no longer required. Just call the bootstrapper code once in the static ctor.

    Cheers,

    kdawg

  6. On second thought, better to keep the GetIsInDesignMode Code and only execute the bootstrapper code if this is true. This prevents run-time running of the bootstrapper code.

    I’m tired, time to crash.

    kdawg

  7. Josh Smith says:

    Blend runs the methods multiple times too, even if it is in the static constructor. It’s running the static constructor of different versions of the same logical type. I assume this has something to do with the fact that Blend doesn’t load your types into a separate AppDomain, which could be unloaded after compilation. Regardless, your initialization code should check to see if it has already been run, just to make sure there’s not a problem.

  8. Andy Earnshaw says:

    Josh,
    You state that “If you decorate an assembly with an attribute, that attribute must be instantiated when the assembly is loaded into memory.”
    As a general statement, this is, unfortunately, not true; otherwise I would be using it for assembly level initialization!!
    Even the static ctor() would not be instantiated until referenced.

  9. Mike Brown says:

    AWESOME find Josh! You could even embed some design-time data in another assembly in an xml file or point it to your test data provider. You, dear sir, are my idol!

  10. This post (http://www.pocketsilicon.com/post/Things-That-Make-My-Life-Hell-Part-1-App-Domains.aspx) came across recently which talks about the unloading and reloading of assemblies within Cider. While I would have expected the previous behavior to cause the same reinitialization problem you describe (and maybe it does!) this sounds like a good culpret to me.

    “Cider uses app domains internally. If we didn’t, every time you built your project we would have to load another set of your compiled assemblies, stacking up the old ones in memory until we exhausted all the memory in your system. This is what most designers in VS (and Blend) do – Cider is one of the first to break new ground here.”

  11. Brian Noyes says:

    I suspect the multi-loading is because of vshost.exe. VS keeps vshost.exe running and reloads your assembly into it after a build so that you are running the latest bits when you debug. Not sure about blend, maybe it does the same.

  12. Josh Smith says:

    @Andy – Great catch. I updated the post to indicate that reflecting over the assembly causing the attribute to be created.

    @Mike – Thanks man!

    @Ryan – Interesting post, thanks for the link.

    @Brian – That makes sense about vshost. I don’t think Blend has an equivalent process, but it handles assembly loading very differently from Cider. No child appdomains in Blend.

  13. Walt Ritscher says:

    @Brian

    I vaguely recall running into this issue with the Visual Studio loading behavior last year. I believe unchecking the ‘Enable the Visual Studio hosting process’ checkbox in the Project proprerty dialog was one way to verify that it was vshost.exe

  14. Roman says:

    Does not work for Blend 3… right? Or am I doing something wrong?

  15. Josh Smith says:

    Correct, it does not work in Blend 3.

Follow

Get every new post delivered to your Inbox.

Join 290 other followers

%d bloggers like this: