Now that people have been kicking the tires of the WPF Designer for Visual Studio 2008 (aka "Cider"), we're starting to see a lot of questions about why perfectly good WPF code doesn't load in the designer. Here's a preview of the topic that will ship in the docs. Hopefully this will help out before frustration and despair set in.
Update: This topic is now posted at MSDN.
---
Windows Presentation Foundation Designer Extensibility for Visual Studio
Troubleshooting WPF Designer Load Failures
The Windows Presentation Foundation (WPF) Designer for Visual Studio includes a sophisticated and extensible visual designer that renders XAML. If your XAML file does not load in the designer, there are several things that you can do to try to understand what is going wrong. This topic describes some tips and techniques to help you troubleshoot WPF Designer load failures.
Note: Many of the techniques in this topic also apply to Expression Blend.
Troubleshooting Steps
The following steps can help you troubleshoot WPF Designer load failures.
Writing Code for Design Time
Ensure that your code runs at design time, as well as run time. If your code runs at design time, do not assume that Application.Current is your application. For example, when you are using Expression Blend, Current is Expression Blend. At design time, MainWindow is not your application's main window. Typical operations that cause a custom control to fail at design time include the following.
There are two approaches to writing code for design time. The first approach is to write defensive code by checking the input parameters to classes, such as value converters. The second approach is to check whether design mode is active by calling the GetIsInDesignMode method.
Checking input parameters for some implementations is necessary because the design environment provides different types for some inputs than those provided by the runtime environment.
Style selectors and value converters usually require one of these approaches to run correctly at design time.
Value ConvertersYour custom IValueConverter implementations should check for null and for the expected type in the first parameter of the Convert method. The following XAML shows a binding to Application.Current that fails at design time if the value converter is not implemented correctly.
<ComboBox.IsEnabled> <MultiBinding Converter="{StaticResource specialFeaturesConverter}"> <Binding Path="CurrentUser.Rating" Source="{x:Static Application.Current}"/> <Binding Path="CurrentUser.MemberSince" Source="{x:Static Application.Current}"/> </MultiBinding></ComboBox.IsEnabled>
The binding raises an exception at design time because Application.Current refers to the designer application instead of your application. To prevent the exception, the value converter must check its input parameters or check for design mode.
The following code example shows how to check input parameters in a value converter that returns true if two input parameters satisfy particular business logic.
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture){ // Check the values array for correct parameters. // Designers may send null or unexpected values. if (values == null || values.Length < 2) return false; if (!(values[0] is int)) return false; if (!(values[1] is DateTime)) return false;
int rating = (int)values[0]; DateTime date = (DateTime)values[1];
// If the user has a good rating (10+) and has been a member for // more than a year, special features are available. if((rating >= 10) && (date.Date < (DateTime.Now.Date - new TimeSpan(365, 0, 0, 0)))) { return true; } return false;}
The second approach to writing code for design time is to check whether design mode is active. The following code example shows a design-mode check instead of the parameter check shown previously.
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture){ // Check for design mode. if ((bool)(DesignerProperties.IsInDesignModeProperty.GetMetadata(typeof(DependencyObject)).DefaultValue)) { return false; }
// If the user has a good rating (10+) and has been a member for // more than a year, special features are available. if((rating >= 10) && (dat.Date < (DateTime.Now.Date - new TimeSpan(365, 0, 0, 0)))) { return true; } return false;}
Style SelectorsYour custom style selectors also must be implemented to run in design mode. The following XAML shows a custom template selector that uses Application.MainWindow at run time to determine which resource is returned as a DataTemplate. At design time, this resource may not be available, so the SelectTemplate override returns null at design time.
<local:TaskListDataTemplateSelector x:Key="myDataTemplateSelector"/><ListBox Width="400" Margin="10" ItemsSource="{Binding Source={StaticResource myTodoList}}" ItemTemplateSelector="{StaticResource myDataTemplateSelector}" HorizontalContentAlignment="Stretch" IsSynchronizedWithCurrentItem="True"/>
The following code shows the implementation of the style selector.
public class TaskListDataTemplateSelector : DataTemplateSelector{ public override DataTemplate SelectTemplate(object item, DependencyObject container) { if (item != null && item is Task) { Task taskitem = item as Task; Window window = Application.Current.MainWindow;
// To run in design mode, either test for the correct window class // or test for design mode. if (window.GetType() == typeof(Window1)) // Or check for design mode: //if (!DesignerProperties.GetIsInDesignMode(window)) { if (taskitem.Priority == 1) return window.FindResource("importantTaskTemplate") as DataTemplate; else return window.FindResource("myTaskTemplate") as DataTemplate; } }
return null; }}
UserControl and Custom Control Resources at Design TimeBy default, UserControl and custom control resources that are available at run time may not be available at design time. When you add your custom controls and user controls to a Page or Window on the design surface, an instance of the control is created. Resources in App.xaml are not available to UserControl and custom control instances loaded on a page or window.
To make your resources available at design time, factor them into a separate resource dictionary and include the dictionary in App.xaml and your control's XAML. Change all StaticResource references to DynamicResource references. The following code example shows how to share a resource dictionary so that its resources are available at design time.
UserControl1.xaml
<UserControl.Resources> <ResourceDictionary> <ResourceDictionary.MergedDictionaries> <ResourceDictionary Source="Dictionary1.xaml" /> </ResourceDictionary.MergedDictionaries> </ResourceDictionary></UserControl.Resources>
App.xaml
<Application.Resources> <ResourceDictionary> <ResourceDictionary.MergedDictionaries> <ResourceDictionary Source="Dictionary1.xaml" /> </ResourceDictionary.MergedDictionaries> </ResourceDictionary></Application.Resources>
Pack URI SyntaxYou should not use application-relative resource references. The following code example shows the application-based syntax, which may fail at design time.
<Image Name="image1" Source="pack://application:,,,/Image1.bmp" />
Such references are relative to the application instead of the DLL. Using an application-relative resource reference in a DLL makes the DLL dependent on resources in the parent application. This approach is fragile and not guaranteed to work at design time.
Instead of using application-relative resource references, add resources to the DLL itself and use component-based resource references. For more information, see "Pack URIs in Windows Presentation Foundation".
The following code examples show the component-based syntax, which is the recommended approach.
<Image Name="image1" Source="/TestHostApp;component/Image1.bmp" /><Image Name="image1" Source="pack://application:,,,/TestHostApp;component/Image1
Programming TipsThe following are some programming tips when you work with the WPF Designer.
Programming Best PracticesThe following are some programming best practices on how to write more robust code for the WPF Designer.