tweaking your wpf code to run in Expression Blend...

beta 1 of Expression Blend ( aka Expression Interactive Designer, codename Sparkle)  went out  a month ago...     I hope you are using it now..  I have used it for a while beforexe it went out, it has been very useful for creating animations, templates, styles, etc.. 

If you have tried to use Blend with existing ( arguably very large) projects coded prior to Blend, you might have seen 'errors' or even crashes when opening your project in blend;  don't give up yet! imho blend is not too unstable, once you understand their design-time rules..    (read below to learn some of these)..

Background:   
Blend is a WPF application and as such it runs all user interface in a single AppDomain;  there fore when you design your app in blend, it tries to load [and run] your code in their executable... so be careful what you feed it :( ...   

How and why does it run your code?
There are two instantiation patterns i see in expresssion:

  • Blend will instantiate the base type of the root element on the design surface.. so for your Window, or UserControl or similar, it is mostly OK..
  • If you have a UserControl (UC) or Custom control you are droppping into a root document ( the root element described above)..  then Blend instantiates YOUR control, and at the minimum runs your constructor ....  
    • if inside your constructor you have code that subscribes to any other events, like   Initialized,  Loaded, or CompositionTarget.Rendering  then you are already telling the UI to call you back for this events [so yet more code to run] ...  if any of your code throws an exception, Blend will throw an error...

Here is an example:  comments in D00BE8

<!-- This is what I referred to above as "root element", here it instantiates Window class instead of your Window1 class, so not your code -->
<

Window xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ExpresssionTests="clr-namespace:ExpresssionTests" x:Class="ExpresssionTests.Window1" Title="ExpresssionTests" Height="377" Width="362">
<Grid>
<!-- Here because you are inside the document (in this case window, it will instantiate your control directly, running the constructor for UserControl1, myTextBox and MyPanel it does not matter if they are user controls or custom controls -->
<ExpresssionTests:UserControl1 Margin="32,51,161,0" VerticalAlignment="Top" Height="108"/>
<ExpresssionTests:MyTextBox Margin="32,21,0,0" VerticalAlignment="Top" Height="26" Text="MyTextBox" TextWrapping="Wrap" HorizontalAlignment="Left" Width="113"/>
<ExpresssionTests:MyPanel Margin="42,0,161,58" VerticalAlignment="Bottom" Height="92"/>
</Grid>
</Window>

So, it calls your constructor (and executes your code).. why should you care? Your app works great when launched stand-alone (or debugger in VS)..  
For a lot of your code, this will be fine...   for that slight percentage that it is not find, there are a few possible bad outcomes from it running your code:

  • Your code does some thing that throws an unhandled exception and crashes Blend..
  • Your code does some thing that throws an exception and blend handles it, but you get a generic error like "Can not create instance of UserControl1"
  • Your code might appear to be running OK in blend but it is consuming resources it should not ( e.g. if you are designing a Video Player, and in your player, you are telling it to "autoplay" when loaded,  so now you have a video playing at design-time.. [this is not only annoying but consuming resources, so your system slows down :( ]

"Jaime, did you not read that 'my code works fine, I don't expect crashes.' "
Hear ya, but there are a few valid scenarios where your perfect code behaves different when run from within blend.. here are a few commone ones:

  • Your application is data-driven and your control assumes the context and data models is available at design-time, but it is not ...  or your app tries to connect to its datasource and it fails ..
  • Your application uses the pack syntax ... [ I will come back to this]
  • Your code tries to access the global Application class or it's MainWindow (but since you are running in Blend, you get Blend's Application class)..

"OK, I get it, how can I get around this?.."
Blend and Cider team added a System.ComponentModel.DesignerProperties class to WPF so you can query if you are in 'design mode'.. 
You can call System.ComponentModel.DesignerProperties.GetIsInDesignMode( DependencyObject element ) with any valid DependencyObject -translated pretty much any visual- , and find out if you are in the designer;

 if you don't have a DependencyObject ( e.g. if you are in your business object and don't want to couple if with UI) then
((bool)DependencyPropertyDescriptor.FromProperty(DesignerProperties.IsInDesignModeProperty, typeof(DependencyObject)).Metadata.DefaultValue)
 can do the trick to tell you if you are in design-mode.. even when you don't have a visual * ..

<sideline> Jim Nakashima and Brian Pepin are good sources for info in this design mode property </sideline>

"This sounds messy! I don't want to be crowding code with design-time checks ... what should I do?"
At first, I thought that too ... but it is really not too bad.. in fact, now I would argue it has made my code slightly better: I used to have projects where there was a bit more code than needed in the constructor.. [this code should have been in the Initialized or Loaded events] ... so you just need to refactor your code a little bit..  you can then use the design-time check to possibly not wire these other while in design-time...

Another lazy workaround I used for those more complex projects with lots of transitions where I did need code in the constructor or had too much code and did not want to compromise run-time code readability w/ design-time checks was a put a very big  try { } catch in the  constructor or event being called...   in the catch, I check if I am in design time and just not bubble ( re-throw ) the exception ....  not ideal but it does the trick.. e.g.

 try
            {             
/// Lots of code would go here...  as long as it gets caught below Expression will be fine.. 
            }
            catch (Exception ex)
            {
// I like to log them so that  I can clean up once a week..
// This will no longer be necessary soon... Blend  will start letting you see the stack traces in the next public build  
// it also re-throws if this is run-time
              ExpressionWorkAround.DesignTimeHelper.LogException(ex);
            }

"... the explanation is fine, but I can't even open my project in Blend... I don't think it is getting far enough to run any code.. " ...  
Almost every time I think this, I look and it is getting far enough,  here is what happens:  Blend opens the solution and then it starts opening the windows I had opened last time in the designer, and crashes while executing my code..  Here are a few tips on opening projects:

  1. You can pass your full project path to blend from command-line ...  when you do this, blend simply opens the project/solution.. It does not automatically open any of your Windows (or scenes) in the designer  ... you will just need to double click in the window/control and it will then open it in the designer [possibly reproducing the crash] ...

    c:\Program Files\Microsoft Expression\Blend Beta 1\Blend.exe "c:\mypath\myproj.csproj"

  2. If you open blend from command line, you can pass the /exceptionlog parameter and if it crashes, it will show you the exception's call stack ...  so you know where to look. This is of course useful even if your project is not crashing when opening.. it will log the exception when ever it happens..

c:\Program Files\Microsoft Expression\Blend Beta 1\Blend.exe /logException   
Then you open your project and run as usual ...

"OK., I am getting it .. any more tips?"
Yes, but getting late and I am going to bed :( so here is the last one..   as mentioned Blend is a WPF app and it loads your code in it.. so  if you are getting design time errors and want to troubleshoot you can open good old Visual Studio, open your project in both VS and blend, and from VS attach to blend and debug, stepping through it, etc...  pretty useful for complex stuff .. 

  1. Open your project in Blend
  2. Open your project in VS ..
  3. From VS attach to Expression Blend ... (Attach Process, proc name is blend.exe ) ...
  4. [Optional] In VS, tell it to break on any thrown Exceptions.. 
    1. via  the Menus: Debug -> Exceptions
    2. Check the "thrown" box i nthe Common Language  Runtine exceptions
  5. [Optional] if don't want to stop at all exceptions, then put a breakpoint in your code..
  6. Now, in Blend close & open the scene (window, user control) you want to debug.
  7. If you are not getting it to hit your breakpoint, some times I have to do a Rebuild all in blend... before it catches on ..

One last remark...  
if you are a designer, or new to Blend and reading this, don't freak yet...   for designers, most of this stuff above happens when devs start putting code in places they might not need to so you are likely not to run into it...  for new projects also, it might not be an issue.. it is  those large projects that have 10s of thousands of lines of code in hand-coded XAML and or C# code...   Also, note that a lot of the troubleshooting tips above get much easier in later versions of Blend (I think beta2)... I mostly wanted you to get the behavior and how blend loads your code.. so you understand what up...

I will try to come back w/ a blend tips & tricks ...   In the mean time, there is a small project with some sample code to illustrate points above here..   Do a search for //TODO: interesting  comments .... you can check them out, run the code, etc...   if you run it simultaneously w/ dbgview, you should be able to see the Debug.WriteLine ()s  that tell you a little of what's going on ...

 Good luck ...

* [disclaimer, I should credit some one for the tip on using IsInDesignMode dep property with out a dep object, but can't recall who it was that shared this.. my guess would have been Unni or John Gossman. Sorry! Do check their blogs though :)