Matt Casto's .NET Journal RSS 2.0
 Saturday, March 14, 2009

In the spirit of Code Share Week, I’ve decided to share a base class we’ve been using for our test classes on my current project.

Specification

We’ve been using a Specification base class for our NUnit test classes, which is kind of following the BDD style of unit testing.

   1: [TestFixture]
   2: public class Specification
   3: {
   4:     [SetUp]
   5:     public void Initialize()
   6:     {
   7:         Mocks = new MockRepository();
   8:         Before_each();
   9:         ReplayAll();
  10:         Because();
  11:     }
  12:  
  13:     [TearDown]
  14:     public void Cleanup()
  15:     {
  16:         After_each();
  17:     }
  18:  
  19:     protected virtual void Before_each() { }
  20:     protected virtual void After_each() { }
  21:     protected virtual void Because() { }
  22:  
  23:     public MockRepository Mocks { get; private set; }
  24:  
  25:     protected T Mock<T>() where T : class
  26:     {
  27:         return MockRepository.GenerateMock<T>();
  28:     }
  29:  
  30:     protected T Stub<T>() where T : class
  31:     {
  32:         return MockRepository.GenerateStub<T>();
  33:     }
  34:  
  35:     protected T Stub<T>(params object[] args) where T : class
  36:     {
  37:         return MockRepository.GenerateStub<T>(args);
  38:     }
  39:  
  40:     protected T Partial<T>() where T : class
  41:     {
  42:         return Mocks.PartialMock<T>();
  43:     }
  44:  
  45:     protected T Partial<T>(params object[] args) where T : class
  46:     {
  47:         return Mocks.PartialMock<T>(args);
  48:     }
  49:  
  50:     protected void Raise(object mock, string eventName, object sender, EventArgs args)
  51:     {
  52:         new EventRaiser((IMockedObject)mock, eventName).Raise(sender, args);
  53:     }
  54:  
  55:     protected virtual void ReplayAll()
  56:     {
  57:         Mocks.ReplayAll();
  58:     }
  59:  
  60:     protected void spec_not_implemented()
  61:     {
  62:         MethodBase caller = new StackTrace().GetFrame(1).GetMethod();
  63:  
  64:         spec_not_implemented(caller.DeclaringType.Name + "." + caller.Name);
  65:     }
  66:  
  67:     protected void spec_not_implemented(string specName)
  68:     {
  69:         Console.WriteLine("Specification not implemented : " + specName);
  70:     }
  71:  
  72: }

A typical Specification

Here’s a typical specification written to test user account creation.

   1: public class When_creating_a_new_user_account : Specification
   2: {
   3:     private IThirdPartyUserService _thirdPartyUserService;
   4:     private IUserDetailController _userDetailController;
   5:     private CreateUserCommand _newUserCommand;
   6:     private UserService _userService;
   7:  
   8:     protected override void Before_each()
   9:     {
  10:         _thirdPartyUserService = Stub<IThirdPartyUserService>();
  11:         _userDetailController = Stub<IUserDetailController>();
  12:         var lookupService = Stub<ILookupService>();
  13:         var logService = Stub<ILogService>();
  14:         var encryptionService = Stub<IEncryptionService>();
  15:         _newUserCommand = new CreateUserCommand();
  16:         _thirdPartyUserService.Stub(tpus => tpus.CreateUser(_newUserCommand)).Return("fakeAuthTicket");
  17:         encryptionService.Stub(es => es.Encrypt(_newUserCommand.Password)).Return(_newUserCommand.Password);
  18:         _userService = new UserService(_thirdPartyUserService, _userDetailController, 
  19:                                         logService, lookupService, encryptionService);
  20:     }
  21:  
  22:     protected override void Because()
  23:     {
  24:         _userService.CreateUser(_newUserCommand);
  25:     }
  26:  
  27:     [Test]
  28:     public void Should_call_the_ThirdParty_user_service()
  29:     {
  30:         _thirdPartyUserService.AssertWasCalled(tpus => tpus.CreateUser(_newUserCommand));
  31:     }
  32:  
  33:     [Test]
  34:     public void Should_call_the_UserDetailController_to_persist_data()
  35:     {
  36:         _userDetailController.AssertWasCalled(udc => udc.Insert(_newUserCommand.Login,
  37:             _newUserCommand.Password, _newUserCommand.Email));
  38:     }
  39: }

Specification using RhinoAutoMocker

One problem we ran into was that the UserService class kept having dependencies added as more functionality was added to the class. We’re using StructureMap as an IoC container, so there’s no impact to code when you add a dependency to a constructor, but it was requiring us to refactor our tests since we’re not using StructureMap to create instances inside of our tests.

StructureMap comes with a separate assembly containing the RhinoAutoMocker which my colleague Jon Kruger suggested would solve our constant refactoring problem. You can use the auto mocker functionality to automatically mock all dependencies of a class. This also cleans up your code because you don’t have to create fields for dependencies that you plan on using in multiple test methods.

   1: public class When_creating_a_new_user_account : Specification
   2: {
   3:     private RhinoAutoMocker<UserService> _mocker;
   4:     private IThirdPartyUserService _thirdPartyUserService;
   5:     private IUserDetailController _userDetailController;
   6:     private CreateUserCommand _newUserCommand;
   7:  
   8:     protected override void Before_each()
   9:     {
  10:         _mocker = new RhinoAutoMocker<UserService>();
  11:         _newUserCommand = new CreateUserCommand();
  12:         _thirdPartyUserService = _mocker.Get<IThirdPartyUserService>();
  13:         _thirdPartyUserService.Stub(tpus => tpus.CreateUser(_newUserCommand))
  14:                                     .Return("fakeAuthTicket");
  15:         _userDetailController = _mocker.Get<IUserDetailController>();
  16:         _mocker.Get<IEncryptionService>().Stub(es => es.Encrypt(_newUserCommand.Password))
  17:                                     .Return(_newUserCommand.Password);
  18:     }
  19:  
  20:     protected override void Because()
  21:     {
  22:         _mocker.ClassUnderTest.CreateUser(_newUserCommand);
  23:     }
  24:  
  25:     [Test]
  26:     public void Should_call_the_ThirdParty_user_service()
  27:     {
  28:         _thirdPartyUserService.AssertWasCalled(tpus => tpus.CreateUser(_newUserCommand));
  29:     }
  30:  
  31:     [Test]
  32:     public void Should_call_the_UserDetailController_to_persist_data()
  33:     {
  34:         _userDetailController.AssertWasCalled(udc => udc.Insert(_newUserCommand.Login,
  35:             _newUserCommand.Password, _newUserCommand.Email));
  36:     }
  37: }

AutoMockingSpecification

I took this a step further and created a base class that is used when you’re only testing one class. It doesn’t add all that much functionality, but it does make your specs look much cleaner.

   1: public class AutoMockingSpecification<T> : Specification
   2:     where T : class 
   3: {
   4:     private bool _replayTheClassUnderTest = false;
   5:  
   6:     public RhinoAutoMocker<T> Mocker { get; set; }
   7:  
   8:     protected override void Before_each()
   9:     {
  10:         base.Before_each();
  11:         Mocker = new RhinoAutoMocker<T>();
  12:     }
  13:  
  14:     protected override void ReplayAll()
  15:     {
  16:         base.ReplayAll();
  17:         if (_replayTheClassUnderTest)
  18:             Mocker.ClassUnderTest.Replay();
  19:     }
  20:  
  21:     public virtual void PartialMockTheClassUnderTest()
  22:     {
  23:         Mocker.PartialMockTheClassUnderTest();
  24:         _replayTheClassUnderTest = true;
  25:     }
  26: }

Specification Using AutoMockingSpecification

Here’s our sample UserService specification refactored to use the AutoMockingSpecification base class.

   1: public class When_creating_a_new_user_account : AutoMockingSpecification<UserService>
   2: {
   3:     private CreateUserCommand _newUserCommand;
   4:  
   5:     protected override void Before_each()
   6:     {
   7:         base.Before_each();
   8:         _newUserCommand = new CreateUserCommand();
   9:         Mocker.Get<IThirdPartyUserService>().Stub(tpus => tpus.CreateUser(_newUserCommand))
  10:                                             .Return("fakeAuthTicket");
  11:         Mocker.Get<IEncryptionService>().Stub(es => es.Encrypt(_newUserCommand.Password))
  12:                                         .Return(_newUserCommand.Password);
  13:     }
  14:  
  15:     protected override void Because()
  16:     {
  17:         Mocker.ClassUnderTest.CreateUser(_newUserCommand);
  18:     }
  19:  
  20:     [Test]
  21:     public void Should_call_the_ThirdParty_user_service()
  22:     {
  23:         Mocker.Get<IThirdPartyUserService>().AssertWasCalled(tpus => tpus.CreateUser(_newUserCommand));
  24:     }
  25:  
  26:     [Test]
  27:     public void Should_call_the_UserDetailController_to_persist_data()
  28:     {
  29:         Mocker.Get<IUserDetailController>().AssertWasCalled(
  30:             udc => udc.Insert(_newUserCommand.Login, _newUserCommand.Password, _newUserCommand.Email));
  31:     }
  32: }

 

I’m pleased with the result, and it seems to be pretty popular with the other developers on our project.

Saturday, March 14, 2009 2:29:41 PM (Eastern Standard Time, UTC-05:00)  #    Comments [0] -

 Friday, March 13, 2009
I'll be speaking at the Cincinnati Silverlight Firestarter event at the Microsoft office in Mason, OH on March 28th.  Following in the Cleveland event's footsteps, this is going to have local speakers and is a great full day introduction to Silverlight.  I'll be giving the Controls and Databinding post-lunch talk again. (Hopefully this time my laptop will work with the projector.)

 

Agenda:

 8:30 AM: Registration

 9:00 AM: An Introduction to Silverlight (Jeff Blankenburg)

 10:30 AM: An Introduction to XAML (Joe Wirtley)

 11:45 AM: Lunch break

 12:30 PM: An Introduction to the Tools (Josh Holmes)

 2:00 PM: An Introduction to Controls & Data Binding (Matt Casto)

 3:30 PM: Server Communication (Sam Nasr)

 4:45 PM: Giveaways and closing


To register for this event, please go to https://www.clicktoattend.com/invitation.aspx?code=136101


Hope to see you there!

Friday, March 13, 2009 9:17:55 AM (Eastern Standard Time, UTC-05:00)  #    Comments [0] -
silverlight
I was reading Shawn Wildermuth and John Papa's blog posts about all of the tools that they use for Silverlight development, and it finally pushed me to put a thought that's been rattling around in my brain into words.

Last weekend I was at the Art & Code conference at CMU in Pittsburg presenting, along with Tim Hibner, an introduction to Silverlight. Being in that environment reminded me of what it was like when I was a student and was looking at various programming languages. One of the things I was interested in doing back then was developing video games. I remember looking into Flash programming and was turned off by the cost of the development environment. To be honest, that was a while ago and I don't remember what the costs were, but I do remember the feeling that I couldn't bother with the hassle of learning the platform.

Developing video games like today's casual Flash games is a huge opportunity for an entry point to learning Silverlight. Just look at all of the Flash game sites out there. There are actually some Silverlight specific game sites getting started now like Silver Arcade and Silverlight Club. In fact, there is currently a Silverlight game development contest with a grand prize of $5000, which should definitely get people interested.

The first place people interested to start with Silverlight are pointed to is the Get Started page on the official Silverlight web site. This page starts out by linking to the Silverlight Tools for Visual Studio 2008 SP1, which in turn requires you to already have Visual Studio 2008 or Visual Web Developer Express installed with Service Pack 1. Assuming the user downloads the Visual Web Developer Express, they have a joy filled hour+ of baby sitting installs. Then, the second step is to download and install the Expression Blend 2 trial, and download and install the Expression Blend 2 Service Pack 1 on top of that. Gah! It goes on from there.

I'm seriously wondering how many people make it that far, or have given up at this point. I think the final straw for me would have been the fact that Blend is only a 30 day trial. What do I do after that? Pay $499 for Expression Blend or $999 for Expression Studio? No way! Even as a professional developer today I wouldn't pay that. And, sure, there's DreamSpark, but that's an extra hassle, and the get started page doesn't even mention it.

I love Silverlight, but its been easy for me since I've been developing on the Microsoft platform for over 10 years and I have access to MSDN downloads. Thank goodness for the fact that Blend is available through that, or else no one would bother with it.

Microsoft has done a great job with making free versions of its tools available lately, but for all the weight they're putting behind Silverlight I don't understand why they don't, at the very least, have an Express version of Blend. I would even suggest that there should be a free version of the entire Expression Suite, although a name like Expression Suite Express is kinda silly. :-)

Friday, March 13, 2009 7:09:13 AM (Eastern Standard Time, UTC-05:00)  #    Comments [10] -
silverlight | tools
 Monday, November 03, 2008
Last Saturday's Silverlight Firestarter event in Cleveland was a blast.  Being able to express my excitement for Silverlight to so many people (around 70 people showed) that had little to no experience with it was unbelievable.

I had problems connecting my Dell Inspiron 1720 laptop to the external display so the projector wouldn't work.  I had the same problem last Tuesday night at the CINNUG presentation, but this time John Stockton was there to save the day.  I was able to present all of my material on his machine without much of a hitch.

Jeff Blankenburg did a top notch job presenting the keynote and improvising in his Tools talk when his Blend install wouldn't work with Silverlight 2 projects.  Sarah Dutkiewicz was inspirational in her XAML basics session - she gave a clear introduction to XAML while being sick and barely able to talk!  Finally, John Stockton's presentation on Server Communicatiion in Silverlight showed why he's the man.  :-D

The project and slide files that I used for the presentations are available from my Sky Drive:

Monday, November 03, 2008 10:10:04 PM (Eastern Standard Time, UTC-05:00)  #    Comments [0] -
silverlight
 Tuesday, October 21, 2008
I'll be speaking at the Cleveland Silverlight Firestarter event on November 1st.  This event is going to have mostly the same structure as the NYC Silverlight Firestarter, but with (mostly) local speakers.  For instance, I'll be filling Shawn Wildermuth's shoes by giving the Controls and Databinding afternoon talk.

 

Agenda:

 8:00 AM: Registration

 9:00 AM: Keynote/Intro to Silverlight 2 (Jeff Blankenburg)

 10:30 AM: XAML Basics (Sarah Dutkiewicz)

 Noon: Lunch break

 1:00 PM: The Tools (Jeff Blankenburg)

 2:30 PM: Controls & Data Binding (Matt Casto)

 4:00 PM: Server Communication (John Stockton)


To register for this event, please go to http://msevents.microsoft.com/CUI/EventDetail.aspx?EventID=1032392883&Culture=en-US


Hope to see you there!

Tuesday, October 21, 2008 1:09:19 PM (Eastern Standard Time, UTC-05:00)  #    Comments [0] -
silverlight
 Tuesday, October 07, 2008
My submission to the Create a Sports Game in Silverlight Contest hosted by Team Zone Sports ended up in second place!

The game is called Ball Blocks because I couldn't come up with a better name at the time.  You play by drawing a square over the balls where the four corners of the square are on top of the same type of ball.  The bigger the square (more balls inside of it) the more points you get and the more time gets added back to the timer.

The game currently has a bug in it where it can freeze the browser during the game over animation when time expires.  This seems to happen more often on slower machines.  It may be because of the spinning textblock ... I'm just not sure.

Because of time spent trying to fix this bug, I didn't get sounds fully implemented and didn't get a far into the game development as I'd hoped.  I'm hoping to spend some more time on it some time soon and get some of the features I'd originally envisioned implemented.

I've created a Ball Blocks project on CodePlex and uploaded the code.  The project has been updated to work in Silverlight 2.0 RC0 so it should be ready once Silverlight 2 is released.

Tuesday, October 07, 2008 10:35:12 AM (Eastern Standard Time, UTC-05:00)  #    Comments [0] -
silverlight
 Monday, April 21, 2008

This is a very simple animation that can be used just about anywhere.

[[ If you're viewing this post through an RSS reader, you won't be able to see the Silverlight example ]]

Following is my Page.xaml for this example.  There was no code needed.

<UserControl x:Class="AnimationSample.Page"
    xmlns="http://schemas.microsoft.com/client/2007" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Width="300" Height="150">
  <Grid x:Name="LayoutRoot" Background="White">
    <Grid.Triggers>
      <EventTrigger RoutedEvent="Grid.Loaded">
        <EventTrigger.Actions>
          <BeginStoryboard>
            <Storyboard x:Name="CrawlingBorder" RepeatBehavior="Forever">
              <DoubleAnimationUsingKeyFrames Storyboard.TargetName="rectangle" 
                Storyboard.TargetProperty="(Shape.StrokeDashOffset)" BeginTime="00:00:00">
                <SplineDoubleKeyFrame KeyTime="00:00:00" Value="0" />
                <SplineDoubleKeyFrame KeyTime="00:00:00.5000000" Value="5" />
              </DoubleAnimationUsingKeyFrames>
            </Storyboard>
          </BeginStoryboard>
        </EventTrigger.Actions>
      </EventTrigger>
    </Grid.Triggers>
    <Rectangle Stroke="Green" StrokeThickness="6" StrokeDashArray="3,2" StrokeDashCap="Round" 
               Margin="20" StrokeDashOffset="0" StrokeLineJoin="Round" x:Name="rectangle">
    </Rectangle>
    <TextBlock FontFamily="Lucida Sans Unicode" FontSize="24" 
               Text="Crawling Border" FontWeight="Bold" 
               HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="#FFDE680A"/>
  </Grid>
</UserControl>
Monday, April 21, 2008 9:55:36 PM (Eastern Standard Time, UTC-05:00)  #    Comments [0] -
silverlight

I gave a talk at the Central Ohio Day of .NET last Saturday about Silverlight 2.  The presentation was written in Silverlight 2 Beta 1 and included examples of functionality built into Silverlight versions 1 and 2, and the beginning steps in creating the presentation itself.

I have much more planned to be added to the presentation, so I created a CodePlex project called PresentLight.  I'm hoping that other people will like the idea of giving a presentation in the technology that they're speaking about, and maybe they'll use the framework or even add their own content!

I uploaded a slightly older version than the one I gave at CODoDN to silverlight.live.com, check it out by clicking on the following preview image.  The XAP is 14 MB so expect a decent wait for everything to load ... I need to reduce the size.  I'll update this post with a better example as soon as I have it available.

Much thanks goes out to Jeff Blankenburg for building the original slide deck that I based this presentation on.  Also, I got the idea from David Sleeckx's WPF presentation, which is an excellent way to get an overview of WPF.

Using the Presentation

You can navigate the slides through the menu on the left, or move forward one slide by clicking on the header area.  A few of the slides don't have much at first, but clicking in the slide area will show text which was talking points for that part of the presentation.  The interactive slides in the middle should be pretty self explanatory - you can modify the XAML in most of the examples to see changes in real time.

The screen shots at the end can be clicked on to view the full size.  I wanted to keep the entire presentation in Silverlight, so I was trying to use screen shots instead of jumping into Visual Studio.  These slides were taking over an hour each to prepare, because I was trying to give each one a different type of animation.  I think they ended up being a little disjointed though - its much more natural to see someone working with the environment than seeing screen shots of some code, then the solution explorer, then XAML.  I'm definitely going to have to give more thought to that area.

Plans for the Future

I plan to have the slides stored in data rather than hard coded in the page code behind.  I'm going to integrate more examples, such as Isolated Storage, Communications with web services and through sockets, and dynamic languages integrated directly into the slides, like I did with the XAML examples.

Also, I'm going to expand more on some of the user controls that I created as part of the application.  I already went into the scrolling textbox control in my last post, so there will be more of that on the way.

Monday, April 21, 2008 9:15:26 PM (Eastern Standard Time, UTC-05:00)  #    Comments [2] -
silverlight
 Sunday, April 13, 2008

The TextBox control included with Silverlight 2 Beta 1 is a welcome addition.  There was no such control in previous versions of Silverlight, including the alpha.

Unfortunately, the TextBox control is very limited at this point.  It does not support scrollbars when it's content is larger than it's size.  You can "scroll" the next by moving the cursor, but that's just enough to make it functional.  Selecting text past what's visible doesn't automatically scroll to the cursor.  Also, there's no text wrapping, although setting the AcceptsReturn property to True will allow line breaks in the text.

I wanted to have a text box with a little more functionality for my projects, so I decided to see what I could do.  What follows is the simple ScrollingTextBox control that I created.  I didn't spend a ton of time on this because I fully expect this "missing" functionality to be included by Beta 2, or at least by the time Silverlight 2 is released.

 

To build this, I first created a Silverlight application project and added a Silverlight control called ScrollingTextBox.

<UserControl x:Class="PresentLight.ScrollingTextBox"
    xmlns="http://schemas.microsoft.com/client/2007" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <Grid x:Name="LayoutRoot">
    <ScrollViewer x:Name="sv" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
      <TextBlock x:Name="hiddenTextBlock" Opacity="0" />
      <TextBox x:Name="tb" AcceptsReturn="True" TextChanged="tb_TextChanged" />
    </ScrollViewer>
  </Grid>
</UserControl>

The internal TextBlock control is only there to serve as a way to get the actual size of the text.  TextBlock controls will automatically resize to fit their contents, so if I keep it's text the same as the internal textbox control, it will resize accordingly.  The default value for Width and Height dependency properties in Silverlight is Auto, which causes this resize behavior.

Since the ScrollViewer control automatically set's it's scrollable region to fit it's contents, it will be resized to fit the internal textblock.  The internal textbox control will automatically resize to fit it's container, since I haven't set any width or height on it.

Then I added a dependency property for setting the control's Text, which takes care of setting the textbox's text value.  Finally, I handled the TextChanged event on the internal TextBox control to resize based on my internal TextBlock's size.

using System.Windows;
using System.Windows.Controls;

namespace PresentLight
{
    public partial class ScrollingTextBox : UserControl
    {
        public string Text
        {
            get { return (string)GetValue(TextProperty); }
            set
            {
                SetValue(TextProperty, value);
                tb.Text = value;
            }
        }

        public static readonly DependencyProperty TextProperty =
            DependencyProperty.Register("Text", typeof(string), 
                               typeof(ScrollingTextBox), null);


        public ScrollingTextBox()
        {
            InitializeComponent();
        }

        private void tb_TextChanged(object sender, TextChangedEventArgs e)
        {
            if (hiddenTextBlock != null)
            {
                hiddenTextBlock.Text = tb.Text;
                tb.Width = hiddenTextBlock.ActualWidth;
                tb.Height = hiddenTextBlock.ActualHeight;
            }
        }
    }
}

The result is a marginally better control.  There are a lot of possible improvements for me to make, such as possibly handing the selection changed event on the textbox to scroll the ScrollViewer to the cursor's position.  That will have to wait for a future post.

Sunday, April 13, 2008 9:41:18 AM (Eastern Standard Time, UTC-05:00)  #    Comments [2] -
silverlight
 Saturday, April 12, 2008

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.

Saturday, April 12, 2008 5:45:08 PM (Eastern Standard Time, UTC-05:00)  #    Comments [1] -
silverlight
About the author/Disclaimer

Disclaimer
The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

© Copyright 2010
Matt Casto
Sign In
All Content © 2010, Matt Casto
Theme based on DasBlog theme 'Business' created by Christoph De Baene (delarou)