OpenLightGroup Blog

rss

Blogs from OpenLightGroup.net


Seeker Sidebar 3 – Parts and States

Live Example:

http://www.adefwebserver.com/Richard/SeekerPartsAndStates/SeekerPartsAndStatesTestPage.html

I’d like to acknowledge that posts by Karen Corby made it possible for me to get to this point in understanding Parts and States.

http://scorbs.com/2008/06/11/parts-states-model-with-visualstatemanager-part-1-of/

image

In this blog I’m going to show you how to create a multi-part templated control with visual states, then how you can manipulate those parts and states in Blend.

If you’ve been following along, you know that the Seeker inherits from a base class named ooNaThing, who provides basic ooNaThing functionality, so ordinarily it would be ooNaThing that would inherit from Control, but for this demo I’m just going to create a custom control named BigSeeker that  inherits from Control directly.

The best way to do this for our purposes is through Visual Studio. There may be an equivalent in Blend, but I don’t know what it is. We’re going to create a Silverlight Templated Control. This not only creates our BigSeeker class, but also creates a file named Generic.xaml, which contains the default ControlTemplate implementation for our new class. And as you will see, it adds code to the BigSeeker class constructor to hook up the C# code to the xaml code in Generic.xaml.

So, with the solution open in Visual Studio, right click on the project, in this case SeekerPartsAndStates, in Solution Explorer and select Add / New Item, which will bring you to the following dialog:

image

After you click Add,  you should see something like this. Generic.xaml has been added to the themes folder and the BigSeeker class is defined in the new file BigSeeker.cs.

image

In the BigSeeker Constructor, the BigSeeker default style is linked to the style in Generic.xaml, through the BigSeeker class. The TargetType of both the Style and the ControlTemplate is “local:BigSeeker”.  Typically a ControlTemplate is wrapped in a Style. This allows a Style and all the properties it defines to be applied simultaneously with the ControlTemplate it defines as one of those properties. If you end up with a ControlTemplate that isn’t wrapped in a style, you’ve probably made a mistake in selecting what to edit.

Now we’re going to open the project in Blend and you’ll see how Blend lists this un-named style as the default style of BigSeeker.

image

Open the project in Blend.

image

Click on Resources, then click the arrow next to Generic.xaml. You can see that [BigSeeker default] is a resource. In this case, it’s a Style Resource wrapped around a ControlTemplate, as I described above.

So, in the picture above, the cursor is over the icon above the ‘Edit resource’ message. Click there to begin adding a default UI to BigSeeker.

I won’t show you the screen that comes up, there’s not much to see, and anyway, we selected the style, what we really want to edit is the ControlTemplate wrapped inside it. There are at least two ways to get there:

Note the breadcrumb at the top of the screen; this is what we get if we click it:

image

Or we can go through the Style in the Objects and Timeline menu:

image

This is one of those points where if you don’t pay attention to what you’re editing it can be too late when you realize later it wasn’t what you thought, so let’s review where we are.

1. We created a new templated control named BigSeeker.

2. We want to define the UI for BigSeeker.

3. We know the UI is defined in Generic.xaml so we click on the Resources tab and then the arrow next to Generic.xaml.

4. We select the icon next to [BigSeeker default] to edit the BigSeeker default style.

5. Now we are about to Edit the [BigSeeker default] current template, meaning the template in effect when the [BigSeeker default] style is applied.

That’s what we want to do, and as Davy Crockett said, “Make sure you’re right and go ahead”. Davy, known to many of you as Daniel Boone, was deeper than people give him credit for. Kill you a grizzly bear when you are only three and you’re typecast for life, or if you’re Fess Parker.

The suspense must be killing you, so go ahead and select Edit Current.

image

Nothing much to see except  that Template now appears under Objects and Timeline, and it apparently contains a Border. Let’s look at the xaml.

    <Style TargetType="local:BigSeeker">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="local:BigSeeker">
                    <Border Background="{TemplateBinding Background}"
                            BorderBrush="{TemplateBinding BorderBrush}"
                            BorderThickness="{TemplateBinding BorderThickness}">
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

 

I don’t think this example is going to get into TemplateBinding, but as far as I understand it, in this case, if the control that is bound to the Template has a Background, BorderBrush, or BorderThickness property, that property will carry through from the control to the template. For instance if the TargetType was RadioButton and the background property of the RadioButton is set, then the Background of my custom representation of a RadioButton, as defined in the ControlTemplate, will have its background property set to the same value.

For more go here: http://msdn.microsoft.com/en-us/library/ee341447(Expression.30).aspx, and I recommend the Zune sample project that comes with Blend. You can find it under

C:\Program Files\Microsoft Expression\Blend 3\Samples\en\Zune3D

But we want to add our own UI, so let’s start by adding a panel, my preference being a Canvas.

image

Drag it right onto the Border object in Object and Timeline.

image

image

Add an ellipse to the Canvas and adjust its properties as shown above, adjust the canvas width and height to the same dimensions.

image

Notice I’ve zoomed in, I didn’t make the ellipse larger.

Add a couple of eyes on top of the body outline; ellipses do not contain other ellipses, all three are on the surface of the canvas. The eye ellipses are just positioned over the body. The Zindex of zero for all three defaults to our benefit because we added the eyes after the body.

Actually as it turns out I did increase the size after I made the screen shots above. I set the Height to 160 and the width to 100. The eyes remain the same.

image

Change the names of the seeker’s visual elements. These are the names that will identify the elements as UI Parts in the Parts and States Model.

In BigSeeker.cs, add the TemplatePart lines you see below. Put them outside the class definition.

 namespace SeekerPartsAndStates
{
    [TemplatePart(Name = "SeekerContainer", Type = typeof(FrameworkElement))]
    [TemplatePart(Name = "SeekerOutline", Type = typeof(FrameworkElement))]
    [TemplatePart(Name = "LeftEye", Type = typeof(FrameworkElement))]
    [TemplatePart(Name = "RightEye", Type = typeof(FrameworkElement))]
  
    public class BigSeeker : Control
    {
        public BigSeeker()
        {
            this.DefaultStyleKey = typeof(BigSeeker);
        }
    }
}

After you do that, build the project. Then in the Parts panel you should see the following. It works because the Name in each TemplatePart matches the name of the corresponding visual element. Notice that each TemplatePart is defined as a FrameworkElement. I hear you asking, since the TemplatePart attributes don’t have any effect on the C# code, what difference does it make?

I think, but I haven’t tried testing it out, so I could be wrong, that you need to define this type at the highest level where it supports interaction with your code. Everything I do is supported by FrameworkElement, so that’s the type I define, for both Canvas and Ellipse. This gives designers maximum flexibility in re-defining the interface, but keep in mind if you change to a more specific type in subsequent versions it will break existing code that relies on the contract you establish in the parts list, so it’s a tradeoff. Or so it seems to me at this point.

image

We should  be able to create an instance of our BigSeeker now. Bring up MainPage on the design surface. Click on the Resources tab at the top right and click the arrow to the left of Generic.xaml. You should see [BigSeeker default].

image

Click on the name, not the icon, and drag to the design surface, you should end up with this:

image

Now I think we’re at the point where we can edit the template and create an alternate visual representation of BigSeeker.

image

So right-click on the seeker, Edit Template / Edit a Copy

image

Give the new style a Name. Notice it’s a style. That’s good, the style wraps the ControlTemplate. If it’s not a style you’ve gone wrong somewhere. I’ve done it and ended up with an un-wrapped ControlTemplate and other bloopers, but I can rarely re-trace my steps. 

As soon as you create the new style it’s available under Resources.

image

If I drag the style onto the design surface…

image

… I now have a default BigSeeker and a BigSeeker with the BigSeekerMutant1 style applied. Let’s look at the xaml:

<UserControl
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
    mc:Ignorable="d" xmlns:local="clr-namespace:SeekerPartsAndStates" x:Class="SeekerPartsAndStates.MainPage"
    d:DesignWidth="400" d:DesignHeight="480">
  <Canvas x:Name="LayoutRoot" Background="White" Margin="50,0,-104,0">
    <local:BigSeeker Canvas.Left="90" Canvas.Top="144" Style="{StaticResource BigSeekerMutant1}"/>
    <local:BigSeeker Style="{StaticResource BigSeekerMutant1}" Canvas.Left="286" Canvas.Top="168"/>
 </Canvas>
</UserControl>

What the what? They both have style BigSeekerMutant1 applied. But we selected edit a copy of the template. Why is this affecting the original BigSeeker? Turns out Blend applies the new style to whatever object you select to get the copy from. I think there’s a way to remove the style ‘visually’, but I’m just going to edit it out in the xaml. (Later I remembered you can just drag [BigSeeker default] from the Resources menu onto the Seeker to change him back to the default style. I’ve blogged about it. Sigh.)

So, we have a new version names BigSeekerMutant1, or more precisely I should say we have a new style named BigSeekerMutant1 that applies a particular control template. So what can we do with it? Let’s find out.

Start by clicking on the icon to the right of BigSeekerMutant1

image

image

Edit the Current Template.

image

Click on Parts, and you’ll see the screen above, where you now have access to the parts. You can see what they are, you can click on them to show which part each corresponds to onscreen and whether or not they are currently assigned. You can edit the element properties as you normally would. You can add and remove elements from Objects and Timeline as you normally would, which means you can replace any of the Parts.

Let’s say we want to replace the SeekerOutline with a Rectangle. Delete the current SeekerOutline. Add a Rectangle to SeekerContainer, then right-click on it and choose Make Into Part of BigSeeker / SeekerOutline:

image

Set the properties of the new SeekerOutline, including setting the ZIndex to –1 to make the eyes visible:

image

Or you can cut to the chase and just change the ellipses to Rectangles in xaml, as I did here for the eyes:

            <Canvas x:Name="SeekerContainer" Height="160" Width="100">
              <Rectangle x:Name="LeftEye" Fill="White" Stroke="Black" Height="16" Width="10" Canvas.Left="37" Canvas.Top="16"/>
              <Rectangle x:Name="RightEye" Fill="White" Stroke="Black" Height="16" Width="10" Canvas.Left="56" Canvas.Top="16"/>
              <Rectangle x:Name="SeekerOutline" Fill="#FFF62727" Stroke="Black" Height="160" Width="100" Canvas.ZIndex="-1"/>
            </Canvas>

And presto-changeo!!!

image

And back on Main Page:

image

So this is pretty huge when you think of it. All I did was map the names of the individual elements to TemplatePart attributes so Blend is able to see them. That’s it, four lines of code. Also notice I can change ellipses to rectangles, I think that’s because I defined the TemplateParts as FrameworkElements instead of Ellipses.

Shockingly, it seems you can’t visually create visual states on ControlTemplates. You can see the states, but the ‘create’ icon is grayed out. The only way I know how to do it is to manually add the xaml as Karen Corby blogs about. I’m going to experiment with different ways to add the visual states before my next blog, such as creating them on a control that has the style applied, then cut and paste the results to the ControlTemplate in Generic.xaml or wherever. As a matter of fact, in the first version of BigSeeker, I created the ellipses right on MainPage, chose GroupInto to put them inside a Canvas, ultimately to be ‘SeekerContainer’. Then I created the Silverlight Templated Control through Visual Studio, which creates Generic.Xaml and the rest. Then I just cut and pasted the Canvas (SeekerContainer) and its contents into the ControlTemplate in Generic.xaml.

I bring this up because for the blog I thought I would show you how to create the UI directly on the default style, but I think it was actually easier going the cut and paste route.

Anyway, as I say, next time will be about VisualStates in the context of ControlTemplates.

Download Code:

http://www.adefwebserver.com/Richard/SeekerPartsAndStates/SeekerPartsAndStates.zip





Comments are closed.