October 26, 2012

WPF style resources bug causes stack overflow in .NET 3.5

Filed under: Development — Tags: , , — Arne Joris @ 8:12 am

XAML logo We recently re-wrote an AutoCAD 2010 application to be entirely based on .NET.  Our application was using Windows Presentation Foundation for the presentation layer, which lets you nicely separate presentation from business logic using the MVVM pattern.  Since this application also had to run in AutoCAD 2009, it had to use the .NET 3.5 version.

It turns out there is a bug in .NET 3.5 (fixed in 4.0) that causes a stack overflow exception when you define two or more default style resources for the same type at the same level.  If you use global resource files and window-specific default styles, you may run into this and its not at all obvious what the problem (or the solution) is.

Designing user interfaces for WPF is similar to designing web interfaces; you can define global styles in a separate file, similar to styles.css for web applications. In our case, the global file resources.xaml defines a default style for ComboBoxes:

  1: <Style TargetType="{x:Type ComboBox}">
  2:   <Setter Property="Width" Value="60"/>
  3:   <Setter Property="Margin" Value="5"/>
  4: </Style>

In our case we keep this global resource file in another project named which we’ll call  TestLibrary for the sake of this post.

The Bug

Every window is defined in its own .xaml file and can specify style settings specific for this window only.

  1: <Window.Resources>
  2:     <ResourceDictionary>
  3:         <ResourceDictionary.MergedDictionaries>
  4:             <ResourceDictionary Source="pack://application:,,,/TestLibrary;component/Resources.xaml"/>
  5:         </ResourceDictionary.MergedDictionaries>
  6:         <Style TargetType="{x:Type ComboBox}" BasedOn="{StaticResource {x:Type ComboBox}}">
  7:             <Setter Property="ItemTemplate">
  8:                 <Setter.Value>
  9:                     <DataTemplate>
 10:                         <TextBlock Text="{Binding Converter={StaticResource EnumDescriptionConverter}}"/>
 11:                     </DataTemplate>
 12:                 </Setter.Value>
 13:             </Setter>
 14:         </Style>
 15:      </ResourcDictionary>
 16: </Window.Resources>

The window first declares the global resource file from the TestLibrary project as a resource dictionary on line 4 and then further defines styles specific to the window. In the example below we inherit the global ComboBox style and specify a ComboBox ItemTemplate to use a converter starting on line 6. Note that none of these styles have a key; we define a default style which will be used by all comboboxes.

When we first ran this code, we got a stack overflow exception on line 4, where Resources.xaml is being included.  A very helpful support MS escalation engineer named Keith Fink found the problem and suggested two workarounds.

The workarounds

The first workaround is to avoid default styles and instead assign a key to your global style and have window specific styles refer to that key name to set a default style, like this:

  1: <Style x:Key="BaseComboBoxStyle" TargetType="{x:Type ComboBox}">
  2:     <Setter Property="Width" Value="60"/>
  3:     <Setter Property="Margin" Value="5"/>
  4: </Style>
  1: <Window.Resources>
  2:     <Resource Dictionary>
  3:         <ResourceDictionary.MergedDictionaries>
  4:             <ResourceDictionary Source="pack://application:,,,/TestLibrary;component/Resources.xaml"/>
  5:         </ResourceDictionary.MergedDictionaries>
  6:         <Style TargetType="{x:Type ComboBox}" BasedOn="{StaticResource BaseComboBoxStyle}}">
  7:            <Setter Property="ItemTemplate">
  8:                  <Setter.Value>
  9:                     <DataTemplate>
 10:                         <TextBlock Text="{Binding Converter={StaticResource EnumDescriptionConverter}}"/>
 11:                     </DataTemplate>
 12:                 </Setter.Value>
 13:            </Setter>
 14:         </Style>
 15:     </ResourceDictionary>
 16: </Window.Resources>

The second workaround is to move the window specific style declarations down a level, from <window.resources> to, for example <grid.Resources>. You can leave the global style definition as they are.

We used the second workaround and coded happily ever after :-)

No Comments »

No comments yet.

RSS feed for comments on this post. TrackBack URL

Leave a comment

Powered by WordPress

Switch to our mobile site