WPF = Code + Markup (Custom pushpins for DeepEarth)

Windows Presentation Foundation has been around for a relative short while. Silverlight brings WPF to the browser, with that the amount of writings on WPF is growing fast. At the moment I’m working on a project which uses Deep Earth, a very cool Silverlight based viewer on Virtual Earth (or any other map provider). Deep Earth is an open source project. Having seen the curly path of this project I want to take a closer look at separation of presentation and logic behind, being one of the fundamentals of WPF.

WPF presentation is written out in markup, which is just another XML document. Logic is written out in C# (or any other .net language).  There are several kinds of logic like business logic and presentation logic. Business logic should be kept as far from WPF as possible but when it comes to presentation logic there are many, many ways to handle that. In markup but also In C#. And sometimes you have to cross the border, C# code manipulating markup or markup providing a hook for your C# code. I will discuss both here.

As a demo I have a simple Silverligth user control. The xaml editor in VS is in it’s current state not that useful. My image is drawn with Expression Blend. After that I copied the markup into VS. (VS and Expression Blend can be integrated, that’s another story). The result looks like this.

Just an ellipse with two lines (the path elements)

I want to be able to set a visual property like the line thickness from code. There are many many ways to do that. According to most sources the way to do it is using databinding in the markup. But that is quite a hassle; not only the markup required also the timing. What if I want to update the property over and over again ? The most informative story I have found was this post by Rick Strahl on his frustrations.

What made me see the light was laying the web aside and browsing Petzolds book Applications = code + markup (this post’s title is a tribute). That book has been critiqued by some as being not the right approach to WPF. For me it works just fine as it perfectly describes the two aspects, code and markup, and how they can communicate. I’m a code guy, at first sight Blend frightens me as a the tool of graphical artist. Anyway, Petzold’s treatment presented a simple solution.

All visual elements have an optional name attribute. By default Expression Blend omits it. Elements with a name do show up in the code behind; elements without one don’t. So after updating the XAML to

<Path x:Name="Line1" HorizontalAlignment="Left" Margin="163.401

<Path x:Name="Line2" HorizontalAlignment="Left" Margin="158.222

I can access the lines from code behind.

public partial class MyFancySilverlightControl : UserControl
{
    public MyFancySilverlightControl()
    {
        InitializeComponent();
    }

    public void ChangeLine(Color color, double thickness)
    {
        Line1.Stroke = new SolidColorBrush(color);
        Line1.StrokeThickness = thickness;
    }
}

The next thing I want to do is use my usercontrols in DeepEarth. DeepEarth does have a nice collection of shape controls to place on a map. Things like polygons to outline a region and pushpins to mark a position. Here the vector graphics used by WPF really shine. For example you can zoom in on a map and outline a city boundary in every detail using a Polygon shape. Zoom out and the boundaries melt into a small dot. Zoom in again and every detail is still there. Imagine doing that with bitmaps..

In the newest version the content of the different shapes is defined in markup templates. The basic markup of a shape is straightforward

<Style TargetType="DeepShapes:GeometryBase">

<Setter Property="Template">

<Setter.Value>

<ControlTemplate TargetType="DeepShapes:GeometryBase">

<Canvas>

<Path x:Name="path"/>

</Canvas>

</ControlTemplate>

</Setter.Value>

</Setter>

</Style>

 

A shape basically contains a single path (line). The template for a pushpin also contains a lot of fancy markup building the image of the pin. The WPF method OnApplyTemplate instantiates the shape. In overrides of this method the DeepEarth classes take further care of the correct size and position of the control on the map.

On a map usercontrols are like pushpins, an image of some kind placed on a certain location. A first shot would be to subclass the DeepEarth Pushpin class and add the markup of my pushpins to the templates of DeepEarth. But that would not be very flexible; in such a scenario every new control requires fiddling with the DeepEarth core assemblies. Besides that it would be hard again to set control properties from code.

Again, working from code provides a solution. And here the markup in the template provides a base for the code. I have added one new class to the DeepEarth shapes, a UserControlHost. This is it’s markup

<Style TargetType="DeepShapes:UserControlHost">

<Setter Property="RenderTransformOrigin" Value="0.5,0.5" />

<Setter Property="AnchorPoint" Value="0.5,0.5" />

<Setter Property="Template">

<Setter.Value>

<ControlTemplate TargetType="DeepShapes:UserControlHost">

<Grid x:Name="LayoutRoot" OpacityMask="#FF000000" Background="#00000000">

</Grid>

</ControlTemplate>

</Setter.Value>

</Setter>

</Style>

Instead of a path the template declares a named grid. The nice thing about a grid control is that other controls can be added to its Children. And that’s just what I do when applying the template

namespace DeepEarth.Shapes
{
    public class UserControlHost : Pushpin
    {

        private readonly UserControl hostedControl;

        public UserControlHost(UserControl hostedControl)
        {
            DefaultStyleKey = typeof(UserControlHost);
            this.hostedControl = hostedControl;
        }


        public override void OnApplyTemplate()
        {
            ((Grid) GetTemplateChild("LayoutRoot")).Children.Add(hostedControl);
            base.OnApplyTemplate();
        }

    }
}

The usercontrol is passed in the constructor.  In the overriden OnApplyTemplate the grid is grabbed using the GetTemplateChild method and the usercontrol is added to its Children. After that OnApplyTemplate of the PushPin base class is fired. It takes care of all the sizing and positioning work. To that method there is no difference between xaml from the original template or xaml from injected usercontrol.

The UserControlHost class is just another shape like a polyline, polygon or the default pushpin. And you can do all the same things with it, like adding it to layers. This way you can host any UserControl in DeepEarth. This snippet will look familiar to anybody working with DeepEarth

UserControl visual;
if (isCurrentPosition)
    visual = new ShipVisual(shipColor, (double) clusteredWp.Course);
else
    visual = new PushpinVisual();

var pin = new UserControlHost(visual);

pin.Point = new Point(clusteredWp.Longitude, clusteredWp.Latitude);
wayPointLayer.Shapes.Add(pin);

That’s it. I hope to have demonstrated that in WPF the border between markup and code is somewhat flexible. Both aspects are very powerful and they can really help each other. Separate well and rule (the deep earth).

This entry was posted in Coding. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • http://petersgekko.codebetter.com pvanooijen

    @John: You are quite right. I lump everything xaml under the brand WPF but in the real world the there are two, slightly different, implementations. The Windows one still bears the label WPF the one web for the web Silverlight. The first name for Silverlight was WPF/E though.

    @Matthew: You are right, the actual properties in my example could be relatively easely done using data binding. The properties are set once. I’m working on a follow up on another demo control where a visual property is updated over and over again in a way I wouldn’t know how to do with databinding . But as said in the pos,t I’m still learning.

  • Matthew Randle

    With your example I cant see the problem with data binding the color and/or thickness of the line (via XAML) to dependency properties defined in the code behind class.

    It doesnt matter that much as you have packaged it up as a user control but removing that dependency would be more in-line with WPF thinking and would allow you to make it into a ‘lookless’ custom control if required.
    Matt

  • http://www.soulsolutions.com.au John

    Nice Article! Its important to point out that WPF is the windows specific desktop technology while Silverlight (originally called WPF everywhere) is a subset that will run on Windows, Mac, Linux, WinMo, Nokia and maybe even Iphone if the Mono guys get there way.
    The XML markup is very similar between WPF and Silverlight (essentially just some missing controls) and is called XAML, everything UI is in the XAML, Blend is a great tool to make XAML.
    Thanks for your feedback on the DeepEarth project!