I haven't been posting much lately because I've been very busy preparing for my session at the Central Ohio Day of .NET. I'm creating a presentation about Silverlight 2.0 Beta 1, but with a twist - the presentation is actually a Silverlight application!
Templates
For the last day or so I've been working on getting my application's main control to use templates to define the interface. This gives me the option to create multiple "skins" for the application.
To accomplish this, I've been referencing two very excellent tutorials by Jesse Liberty and Shawn Burke. But, as usual for me, I ran into a problem that sucked up a ton of time.
The Problem
I was trying to set up my template to include buttons for navigation between slides. I started by building a simple XAML interface for the buttons, like so.
<StackPanel Orientation="Horizontal">
<Button Height="75" Width="75" Margin="0,0,8,0">
<TextBlock Text="First" HorizontalAlignment="Center" VerticalAlignment="Center" />
</Button>
<Button Height="75" Width="75" Margin="0,0,8,0">
<TextBlock Text="Prev" HorizontalAlignment="Center" VerticalAlignment="Center" />
</Button>
<Button Height="75" Width="75" Margin="0,0,8,0">
<TextBlock Text="Next" HorizontalAlignment="Center" VerticalAlignment="Center" />
</Button>
<Button Height="75" Width="75">
<TextBlock Text="Last" HorizontalAlignment="Center" VerticalAlignment="Center" />
</Button>
</StackPanel>
And that looks good enough for this example.
Then I took that same XAML and put it into a template stored in the application resources (in App.xaml).
<Application.Resources>
<Style x:Key="TestTemplate" TargetType="pl:Presentation">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="pl:Presentation">
<StackPanel Orientation="Horizontal">
<Button Height="75" Width="75" Margin="0,0,8,0">
<TextBlock Text="First" HorizontalAlignment="Center" VerticalAlignment="Center" />
</Button>
<Button Height="75" Width="75" Margin="0,0,8,0">
<TextBlock Text="Prev" HorizontalAlignment="Center" VerticalAlignment="Center" />
</Button>
<Button Height="75" Width="75" Margin="0,0,8,0">
<TextBlock Text="Next" HorizontalAlignment="Center" VerticalAlignment="Center" />
</Button>
<Button Height="75" Width="75">
<TextBlock Text="Last" HorizontalAlignment="Center" VerticalAlignment="Center" />
</Button>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Application.Resources>
And modified my Page.xaml to have my Presentation UserControl class (modeled after Shawn Burke's tutorial) with the template applied as a style.
<local:Presentation x:Name="PresentationControl" Style="{StaticResource TestTemplate}" />
But running this treated me to a browser window that's stuck loading. My break point in OnApplyTemplate in my Presentation class never got hit. Pausing Visual Studio didn't tell me anything, so I knew it wasn't stuck in a loop. This had me stuck for several hours.
The Solution
What I eventually found was that certain things that are perfectly acceptable XAML in a user control won't work in a template. Furthermore, if Silverlight encounters these elements in the template it just hangs.
The problem XAML was the buttons. Instead of including your actual buttons in your template, you should instead create a separate template for the buttons, and reference that as your button style.
<Style x:Key="TestButton" TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Button Width="75" Height="75">
<Button.Content>
<ContentPresenter Content="{TemplateBinding Content}"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Button.Content>
</Button>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Then modify the previous template to use the new button template.
<Application.Resources>
<Style x:Key="TestTemplate" TargetType="pl:Presentation">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="pl:Presentation">
<StackPanel Orientation="Horizontal">
<Button x:Name="FirstButtonElement" Content="First"
Style="{StaticResource TestButton}" Margin="0,0,8,0" />
<Button x:Name="PreviousButtonElement" Content="Prev"
Style="{StaticResource TestButton}" Margin="0,0,8,0" />
<Button x:Name="NextButtonElement" Content="Next"
Style="{StaticResource TestButton}" Margin="0,0,8,0" />
<Button x:Name="LastButtonElement" Content="Last"
Style="{StaticResource TestButton}" />
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Application.Resources>
Finally, this works. It looks the same, but now I can take the template and make it look much nicer if I want.
Hopefully this tip will help others working on templating in Silverlight avoid the pitfalls I ran into.