My brother wrote a slick  lens panel control with Silverlight 2 recently. He modeled it on the look and behaviour of the Mac OSX Leopard dock. It was so slick, I had to write my own implementation. You can check it out here. After I got it up and running we decided to do a kind of distributed (by about 800 miles) pair programming session to see if we could come up with cleaner ways to structure our lens panel controls.

I'm not going to go into the low-level implementation details in this article. The behaviour of the control is pretty much the same as the Mac dock control. Kevin has described the high-level approach we ended up with on his own blog, but I will highlight some features of XAML we used to make the lens panel controls more easily extendable.

When we finished refactoring our code, both of our lens panel controls ended up making use of the ItemsControl, data templates, data binding and dependency injection though XAML, but neither of our controls started out that way.

Aidan's Mac OSX Leopard dock style Silverlight control

Dependency Injection

The first structural decision I made when I got far enough in to the code to start displaying items was to try to separate the source of the panel items from the lens panel control itself. The most basic way to do this is have a base class with the generic lens panel behaviour baked in, and then maybe derive versions for showing lens panel items from various different sources like Flickr, iStockPhoto, or custom menu items.

While this approach works, it tightly couples the base lens panel class with all other derived lens panels. What if you wanted to further customise the lens panel to display the individual items differently? You could derive some more classes with the new visuals baked in but again, this tightly couples even more classes together.

My first solution to this problem was to separate out each of the responsibilities into their own classes. The LensPanel class contained the logic to arrange and display the panel items. The LensPanel had a public property of type IPanelItemProvider. This allowed me to implement any number of concrete providers without directly coupling them to the lens panel control itself.

In addition to this, the providers contained a public property of type IPanelItemVisualFactory. This allowed me to further decouple the visual aspects of the panel items from the provider. All of these dependencies can be injected directly using XAML. Below is roughly how my first solution looked.

<LensPanel:LensPanel Width="1000" Height="300" NumberOfItems="12" >
    <LensPanel:LensPanel.PanelItemProvider>
        <LensPanel:iStockPanelItemProvider>
            <LensPanel:iStockPanelItemProvider.PanelItemVisualFactory>
                <LensPanel:ReflectionPanelItemVisualFactory />
            </LensPanel:iStockPanelItemProvider.PanelItemVisualFactory>
        </LensPanel:iStockPanelItemProvider>
    </LensPanel:LensPanel.PanelItemProvider>
</LensPanel:LensPanel>

The above solution has some nice benefits to offer. First, it makes use of the Single Responsibility Principle (SRP). Each of the objects has a single clear responsibility. The lens panel only lays out panel items. The panel item provider only provides panel items. The panel item visual factory only creates panel item visuals. Second, it also makes use of the Open/Closed Principle (OCP). This means that the solution is open for extension (by creating new concrete provider and factory objects) but closed for modification (providing new concrete providers and factories requires no modification of the base control). Third, this solution is very unit testable.

But it turns out there's a much simpler way to implement the same separation of concerns by exploiting several features already built-in to Silverlight 2.

Data Binding And Templates

Rather than creating the provider interface, IPanelItemProvider, and then managing the connection of the items from the provider with the items in the lens panel, we decided to exploit Silverlight's data binding support. Both of our controls derive from the Panel class and it wasn't immediately obvious how we were going to bind directly to an items list within the panel. Kevin discovered the ItemsControl which makes this very simple.

The ItemsControl allows you to create a panel control where you can specify a panel template for the type of panel you want, in addition to a data template for the individual panel items. With only a small amount of refactoring, and without losing any of the benefits of our earlier approaches, we had reworked our solutions to use the ItemsControl. Below you can see I'm creating an ItemsControl and specifying my custom LensPanel class as the panel template and my custom LensPanelItem class for the item template.

<ItemsControl x:Name="_lensPanelControl" ItemsSource="{StaticResource iStockPhotoProvider}">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <l:LensPanel />
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <l:LensPanelItem />
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

In addition to specifying the templates for the panel and panel items, I'm also binding the ItemsControl to an instance of the iStockPhotoProvider declared in the local resources. I chose to derive the iStockPhotoProvider directly from ObservableCollection so that once it is bound to the ItemsControl, any modifications to it result in an update of the ItemsControl. Again, this provides the same benefits as before. The provider is loosely coupled to the lens panel control. Below is the declaration for the concrete photo provider.

<UserControl.Resources>
    <l:iStockPhotoProvider x:Key="iStockPhotoProvider" />
</UserControl.Resources>

This makes it possible for a designer to swap out the provider with different providers by simply modifying the XAML. No code changes are required.

Conclusion

So far, I've been very impressed with WPF and Silverlight 2. It's very obvious that the features of these frameworks are based on solid practical foundations. I found Chris Anderson's book Essential Windows Presentation Foundation (WPF) (Microsoft .NET Development Series) to be an excellent guide to exploring how to use these features in practical ways. Also, he describes at a low level (and in very simple terms) the motivation behind some of these features and exposes details about their implementation. This was a big help in transitioning from a Windows Forms way of thinking to the more modern approach suggested by WPF and Silverlight 2.


I just finished my first Silverlight 2 Beta application. You can see the demo here. I started by writing a simple physics demo using C# and WinForms. I then ported this to WPF and from there ported it to Silverlight. I've tested the Silverlight version on IE 7, Firefox 3.0.1 and Safari on the Mac. It appears to run exactly the same in all browsers. The WPF version appears to run slightly better than the Silverlight version.

WinForms to WPF

I only had to make two changes to the code to port from WinForms to WPF.

  1. Animation Loop. In the WinForms version, the animation loop is implemented using a custom application run loop and some timing code. In WPF the animation loop is implemented by hooking up to the Composition Target Rendering event.
  2. Triangle Rendering. In the WinForms version, the Graphics object (provided by the form Paint method) was referenced directly to fill traingles. In the WPF version, I refactored this out to a Renderer interface and implemented a custom WPF version that added polygons to the Children container of a canvas object.

The WinForms animation loop looks like this.

while (!isClosing)
{
    long frameStartTime = GetTime();
 
    Application.DoEvents();
    _form.UpdateForm(ref isClosing, IntervalInMilliseconds);
 
    long sleepTime = CalculateSleepTime(frameStartTime);
    Thread.Sleep((int)sleepTime);
}

The code below shows the WPF version. Notice the Rendering method is hooked up to the Composition Target Rendering event.

public Window1()
{
    InitializeComponent();
    CompositionTarget.Rendering += new EventHandler(CompositionTarget_Rendering);
 
    _viewPort = new ViewPort(90, 400, 400);
    _simulation = new SimulationEngine(_viewPort);
    _intervalInMilliseconds = 16;
}
 
void CompositionTarget_Rendering(object sender, EventArgs e)
{
    _simulation.run(_intervalInMilliseconds);
    _simulation.paint(new WpfRenderer(myCanvas));
}

The Renderer interface and WPF version are shown below. This code shows how to draw triangles at a very high level in WPF. A more efficient way to do this would be to use the Visual Layer directly.

public interface Renderer
{
    void BeginRender();
    void EndRender();
    void DrawTriangle(Point[] points, Color color);
}
 
public class WpfRenderer : Renderer
{
    private Canvas _canvas;
 
    public WpfRenderer(Canvas canvas)
    {
        _canvas = canvas;
    }
 
    public void BeginRender()
    {
        _canvas.Children.Clear();
    }
 
    public void EndRender() { }
 
    public void DrawTriangle(Point[] points, Color color)
    {
        Polygon triangle = new Polygon();
 
        triangle.Points = new PointCollection(points);
        triangle.Fill = new SolidColorBrush(color);
 
        _canvas.Children.Add(triangle);
    }
}

WPF to Silverlight 2 Beta

Silverlight seems to be converging with WPF slowly but surely. Although, porting to Silverlight wasn't as straight forward as porting to WPF. I had to make two additional changes to the code to port from WPF to Silverlight.

  1. Again, the animation loop. I've been told that when Silverlight 2 is finally released it will have the same Composition Target Rendering event support as WPF. Currently it does not, so I used a customised StoryBoard to implement the animation loop. This approach seems to be quite common.
  2. The Winforms/WPF version uses the XmlSerializer to load the 3D mesh data. As far as I can see, XmlSerializer is not supported in Silverlight 2 Beta (the project refused to accept a reference), so I rewrote the loader using XDocument instead.

The code below shows the main parts of the Storyboard.

public StoryboardAnimationLoop( FrameworkElement parent, double frameDuration )
{
    _animationLoop.Duration = TimeSpan.FromMilliseconds( frameDuration );
    _animationLoop.SetValue( FrameworkElement.NameProperty, "animationloop" );
    parent.Resources.Add( "animationloop", _animationLoop );
    _animationLoop.Completed += new EventHandler( animationLoop_Completed );
}
 
void animationLoop_Completed( object sender, EventArgs e )
{
    if( _stopped )
        return;
    base.Tick();
    (sender as Storyboard).Begin();
}

The demo uses my own physics code and 3D pipeline. I'm not using any external libraries. If you're not experienced with writing physics code or 3D code for that matter, never fear. Check CodePlex for some open source libraries to help get you started if this is the type of application you're interested in writing.