IdentityMine

| Tags: Laurent Bugnion, Silverlight, WPF

originally posted by Laurent Bugnion: (link) - please comment at original post

We already talked often about providing design time data to your designers in Windows Presentation Foundation and in Silverlight, thus enabling them to work visually in design editors such as Expression Blend or the Visual Studio designer (codenamed Cider).
The goal here is very simple: Designers should see something on their design surface. This sounds simple, but it can get quite tricky. Since Blend and Cider run (parts of) your application’s code to render the WPF or Silverlight elements, you need to make sure that the whole code can be executed safely. For some simple operations, it is not a problem (for example, getting today’s date and time is something that Blend and Cider can do easily) but others are more difficult. Here are two examples off the top of my head:

  • Connecting to a database to get data: Since the code that runs in Blend or Cider is executed in a different context than in the real application, this kind of code will fail in Blend/Cider and should be replaced by something else.
  • Getting data from the internet: Here too, most attempts to connect to the internet from Blend/Cider will fail. In the best case you will still see your data controls, but they will be empty. In the worst case you might even get an exception in Blend, which will hide everything on the design surface.

Detecting this kind of issues requires a bit of experience, and to be very systematic. Thankfully you can easily attach a debugger to Expression Blend and execute the code from your application that Blend is running (how else did you think I found out how Blend works and developed my MVVM Light Toolkit? ;)) Attaching the debugger to Blend will be the topic for a next article.

Isolating/Replacing the code

Once you detected the offending lines in your code (and if you do WPF and Silverlight right, this will probably be within your View Model classes), you should provide something else to Blend/Cider to execute. But how can you make sure that you are executing this code in a design time editor and not at runtime?

In fact this is a bit tricky. The problem is that there are a few, very different scenarios that need to be supported:

Running environmentIsInDesignMode
WPF element within Expression BlendTrue
Silverlight element within Expression BlendTrue
WPF element within CiderTrue
Silverlight element within CiderN/A (no Silverlight designer in Visual Studio for now)
WPF application (runtime)False
Silverlight application (runtime)False
WPF user control embedded in a Windows Forms application (runtime)False
WPF user control embedded in a Windows Forms application within BlendN/A (Blend can open, but not render Windows Forms applications)
WPF user control embedded in a Windows Forms application within CiderTrue

Yeah, I am pretty sure that many of you guys forgot that WPF user controls can be embedded in a Windows Forms application. I am guilty of not having tested this particular case in my MVVM Light Toolkit, and the IsInDesignMode property will return true even when the WinForms application is running, forcing the WPF user control to use design time data. Not good (this particular bug is corrected already and will be published in the next few days with V1.1.1 of the MVVM Light Toolkit).

Providing the IsInDesignMode property

I spent a bit of time talking to friends and checking the web, trying to find a unified code that would work in all situations. As much as I can, I try to get the exact same code to run in WPF and in Silverlight (most of the MVVM toolkit’s code is actually shared between WPF and SL), but in that particular case, nothing seemed to work reliably in both environments. So my solution is:

01.private static bool? _isInDesignMode;
02.
03./// <summary>
04./// Gets a value indicating whether the control is in design mode (running in Blend
05./// or Visual Studio).
06./// </summary>
07.public static bool IsInDesignModeStatic
08.{
09. get
10. {
11. if (!_isInDesignMode.HasValue)
12. {
13.#if SILVERLIGHT
14. _isInDesignMode = DesignerProperties.IsInDesignTool;
15.#else
16. var prop = DesignerProperties.IsInDesignModeProperty;
17. _isInDesignMode
18. = (bool)DependencyPropertyDescriptor
19. .FromProperty(prop, typeof(FrameworkElement))
20. .Metadata.DefaultValue;
21.#endif
22. }
23.
24. return _isInDesignMode.Value;
25. }
26.}

Known issues

There is one known issue with this code though: When you open the WinForms designer with the embedded WPF user control in Cider, the code returns false, and thus the runtime mode data is used. This might be a problem because runtime data can crash the designer and nothing will be rendered. After turning the problem in my head a few nights, I decided that this would just be a documented bug. If you are in the situation where you must absolutely use the Windows Forms designer in Cider, and you have an embedded WPF user control in your window, and the designer crashes, I recommend commenting out the code that causes the crash, designing your WinForms window, and then restoring the code. This is annoying, granted, but I don’t have a better solution right now. If anyone knows a way around this problem, please feel free to leave a comment!!

Usage

This property is used over and over again as soon as you do development in Blend. It makes sense to expose it in a way that makes it easy to use.

In my MVVM Light Toolkit, the property is a static property of the ViewModelBase class. This makes it very easy to use in the source code. However, binding to a static property is a bit cumbersome in WPF and impossible in Silverlight. So I also provide a non static property, that is just a wrapper around the static one. The full code is found below. Using the property becomes:

In XAML:

01.<Window x:Class="MvvmLight6.MainWindow"
02. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
03. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
04. Height="400"
05. Width="300"
06. DataContext="{Binding Main, Source={StaticResource Locator}}">
07.
08. <Window.Resources>
09. <BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
10. </Window.Resources>
11.
12. <Grid>
13. <TextBlock Text="Design mode only!!"
14. Visibility="{Binding IsInDesignMode,
15. Converter={StaticResource BooleanToVisibilityConverter}}" />
16. </Grid>
17.</Window>

In C# (within the View Model)

01.public MainViewModel()
02.{
03. if (IsInDesignMode)
04. {
05. // Code runs in Blend -->
06. // create design time data.
07. }
08. else
09. {
10. // Code runs in runtime -->
11. // Connect to DB, services, etc...
12. }
13.}

In C# (outside of the View Model)

01.public MainWindow()
02.{
03. this.InitializeComponent();
04.
05. if (!ViewModelBase.IsInDesignModeStatic)
06. {
07. this.Closing += (s, e) => ViewModelLocator.Dispose();
08. }
09.}

The full code

The whole property, including static and non-static accessor is:

01.private static bool? _isInDesignMode;
02.
03./// <summary>
04./// Gets a value indicating whether the control is in design mode (running in Blend
05./// or Visual Studio).
06./// </summary>
07.public static bool IsInDesignModeStatic
08.{
09. get
10. {
11. if (!_isInDesignMode.HasValue)
12. {
13.#if SILVERLIGHT
14. _isInDesignMode = DesignerProperties.IsInDesignTool;
15.#else
16. var prop = DesignerProperties.IsInDesignModeProperty;
17. _isInDesignMode
18. = (bool)DependencyPropertyDescriptor
19. .FromProperty(prop, typeof(FrameworkElement))
20. .Metadata.DefaultValue;
21.#endif
22. }
23.
24. return _isInDesignMode.Value;
25. }
26.}
27.
28./// <summary>
29./// Gets a value indicating whether the control is in design mode (running under Blend
30./// or Visual Studio).
31./// </summary>
32.[SuppressMessage(
33. "Microsoft.Performance",
34. "CA1822:MarkMembersAsStatic",
35. Justification = "Non static member needed for data binding")]
36.public bool IsInDesignMode
37.{
38. get
39. {
40. return IsInDesignModeStatic;
41. }
42.}

Remember to please comment at original post: (link)

Tweet about this on TwitterShare on FacebookShare on Google+Share on LinkedInPin on PinterestShare on RedditShare on TumblrEmail this to someoneDigg thisFlattr the authorShare on StumbleUpon

Comments are closed.