Using the ImplicitStyleManager with Headered controls

I’ve been playing with the Silverlight toolkit (released at PDC) this week and ran across a pretty nasty edge case related to HeaderedContentControl and the implicit style manager.  If you create something like this, where the Header is set to a Visual of some kind:


<UserControl x:Class=”SilverlightPrototype.Page”
    xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation”
    xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml”
    xmlns:sys=”clr-namespace:System;assembly=mscorlib”
    xmlns:Controls=”clr-namespace:Microsoft.Windows.Controls;assembly=Microsoft.Windows.Controls”>

<StackPanel x:Name=”LayoutRoot” Background=”White”>
   <Button Click=”Button_Click” Content=”Change Style” />
   <Controls:HeaderedContentControl>
      <Controls:HeaderedContentControl.Header>
         <TextBlock Text=”Header” Foreground=”DarkBlue” FontWeight=”Bold” />
      </Controls:HeaderedContentControl.Header>
      <TextBlock Text=”Body” />
   </Controls:HeaderedContentControl>
</StackPanel>


</UserControl>


And then you change the style of the headered control when the button is clicked:


private void Button_Click(object sender, RoutedEventArgs e)
{            
    Uri uri = new Uri(@”SilverlightPrototype;component/theming/simplestyle.xaml”, UriKind.Relative);
    ImplicitStyleManager.SetResourceDictionaryUri(LayoutRoot, uri);
    ImplicitStyleManager.SetApplyMode(LayoutRoot, ImplicitStylesApplyMode.OneTime);
    ImplicitStyleManager.Apply(LayoutRoot);
}


Where the style is just a simple ContentPresenter or ContentControl for the header and the body:


<Style TargetType=”Controls:HeaderedContentControl”>
   <Setter Property=”Template”>
      <Setter.Value>
        <ControlTemplate TargetType=”Controls:HeaderedContentControl”>
           <StackPanel Orientation=”Horizontal”>
              <ContentPresenter Content=”{TemplateBinding Header}” ContentTemplate=”{TemplateBinding HeaderTemplate}” Margin=”10″ />
              <ContentPresenter Content=”{TemplateBinding Content}” ContentTemplate=”{TemplateBinding ContentTemplate}” Margin=”10″ />
           </StackPanel>
        </ControlTemplate>
     </Setter.Value>
   </Setter>
</Style>


Silverlight will crash deep in the DependencyProperty.SetValue code with an ArgumentException indicating that the TextBlock for the header is invalid.  The issue appears to be that the TextBlock defined as the header content is already part of the visual tree and therefore cannot be bound to the new control template.  Content works fine so there is some other path being taken for that property.


The workaround is pretty easy, just use a non-visual in the header and then supply a DataTemplate to give it the appropriate visual tree.  Silverlight will re-create the visual tree each time from the data template so we don’t have the reuse issue:


<StackPanel x:Name=”LayoutRoot” Background=”White”>

<StackPanel.Resources>
   <!— Header works as long as template is supplied – i.e. non-visual element applied directly to header –>
   <DataTemplate x:Key=”HeaderTemplate”>
      <TextBlock Foreground=”DarkBlue” FontWeight=”Bold” Text=”{Binding}” />
   </DataTemplate>
</StackPanel.Resources>

<Button Click=”Button_Click” Content=”Change Style” />

<Controls:HeaderedContentControl HeaderTemplate=”{StaticResource HeaderTemplate}”>
   <Controls:HeaderedContentControl.Header>
      <sys:String>Header Text</sys:String>
   </Controls:HeaderedContentControl.Header>

   <TextBlock Text=”Body” />
</Controls:HeaderedContentControl>

</StackPanel>


If you need more than a string, then just define an object and assign that instead – for example:


class HeaderStuff
{
   public string Text { get; set; }
   public bool IsChecked { get; set; }
}

<DataTemplate x:Key=“HeaderStuff”>
   <StackPanel>
      <Rectangle Fill=“Blue” Stroke=“Yellow” Width=“50” … />
      <CheckBox IsChecked=“{Binding IsChecked}” Content=“{Binding Text}” />
   </StackPanel>
</DataTemplate>



<Controls:HeaderedContentControl HeaderTemplate=“{StaticResource HeaderStuff}”>
   <Controls:HeaderedContentControl.Header>
      <me:HeaderStuff IsChecked=“True” Text=“This is a checkbox” />
   </Controls:HeaderedContentControl.Header>
</Controls:HeaderedContentControl.Header>



About Mark Smith

Windows systems developer with low-level operating system, threading and .NET experience. For the past several years I have been involved with WPF/Silverlight and most recently iOS and Android. I have worked as an architect and designer for several companies and am particularly interested in client/server and graphical solutions. Specialties:.NET, WPF, Silverlight, Windows, iOS