OpenLightGroup Blog

rss

Blogs from OpenLightGroup.net


The Seeker: Sidebar 2 – Blendable Seeker via ControlTemplate

Live Example:

http://www.adefwebserver.com/Richard/TemplatedSeeker/TemplatedSeekerTestPage.html

Probably the most significant benefit of using a ControlTemplate to contain your object’s UI is that the appearance can then be managed by designers without ever touching a line of code. Maybe I feel that way because I’m anxious to get past this visual stuff and back to what the ooNaCreatures can do, not how they look, which kind of proves my point. Anyway, to prove my claim that the Well-Templated Seeker can be changed visually without touching any code, I’ll demonstrate:

Here we are in MainPage.xaml with a seeker named defaultSeeker in the design window.

image

We right-click on defaultSeeker and  choose to edit a copy of its template.

image

You’ll see the following dialog. Give the new resource a name and select where to save it. I like using a resource dictionary because it feels less messy and in this case it makes sense because I can easily move the resources between projects.

image

Select VisualMe, which is the name of the Ellipse control, in the Objects and Timeline list and the Properties tab on the far right.

image

Change the color to a Prey shade of green and set the height to 40.

image

Because we were working on defaultSeeker in MainPage, if we take a look now we find that he’s been decorated with a SeekerInPreysClothing style.

<Canvas x:Name="ooBerCanvas" Height="600" Width="800" Background="Aqua" HorizontalAlignment="Left" VerticalAlignment="Top">
    <ooNa:Seeker x:Name="defaultSeeker" Canvas.Left="48" Canvas.Top="152" 
           Style="{StaticResource SeekerInPreysClothing}" />
</Canvas>

Now I could just edit the xaml to remove the style, this is ‘defaultSeeker’ after all, and create a new ‘camouflaged seeker’ to apply it to but I want to keep this as visual as possible, so now I’m going to blow your mind.

On the right side of the screen, under the Resources tab, click the arrow on Generic.xaml to display the [Seeker default] style resource.  Left click right on the name, not the icon, hold down the button, drag it over defaultSeeker and drop it.

image

Select ‘Style’ on the menu that appears:

image

Boom goes the dynamite!!! Thank yewww!!!

image

I’m not going to do the screen shots because it’s too repetitious, but to create another Seeker and apply the SeekerInPreysClothing style (it’ll be there in the download):

1. Either on the design page or right on Objects And Timeline, copy and paste defaultSeeker onto ooBerCanvas to create a copy with the default name of defaultSeeker_Copy. You’ll probably have to drag the copy off of the original so you can see both.

2. Now under the Resources tab on the right, click the arrow on SeekerResourceDictionary.xaml, and SeekerInPreysCothing will appear.

3. Drag it onto defaultSeeker_Copy just like you dragged [Seeker default] from Generic.xaml to transform defaultSeeker back to his normal self.

I just remembered there’s an easier way, skip steps 1 and 2 and do the drop onto some empty part of the design surface in step 3. Choose ‘Seeker’ on the menu that appears.

There are a bunch of ways you can essentially cut and paste the code to accomplish this. I chose to keep it visual to prove the point that the designer doesn’t have to touch a line of code.

Before I get to the code, I want to point out that this is only the most primitive usage of ControlTemplate.  There’s also VisualStates for one, and if you implement parts and states, you can compose your object of many parts, all of which can be changed by the designer, or so I”m told. I hope to find out very soon.

So here’s the code:

First of all, the seeker is not just another pretty ooNaFace. He comes from hard-working base classes that support the Position property.

namespace TemplatedSeeker.ooNaClasses
{
    public abstract class ooNaThing : Control
    {
        // Dependency properties declaration
        public static readonly DependencyProperty PositionProperty =
            DependencyProperty.Register("Position",
                                           typeof(Point),
                                           typeof(ooNaThing),
                                           new PropertyMetadata(OnPositionChanged));
        public ooNaThing()
        {
            Position = new Point(Convert.ToDouble(this.GetValue(Canvas.LeftProperty)),
                                    Convert.ToDouble(this.GetValue(Canvas.TopProperty)));
        }
        public Point Position
        {
            get { return (Point)this.GetValue(PositionProperty); }
            set { this.SetValue(PositionProperty, value); }
        }
        private static void OnPositionChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            ooNaThing thing = sender as ooNaThing;
            if (thing == null)
                return;
            thing.SetValue(Canvas.LeftProperty, ((Point)e.NewValue).X);
            thing.SetValue(Canvas.TopProperty, ((Point)e.NewValue).Y);
        }
    }
}

Allow me to point out that ooNaThing inherits from Control, which puts us on a footing with Button and all those other uptown controls. This is the hook into the same mechanism you’d use to skin those controls.

So anyhoo, we work our way down (or up) to Seeker which has an actual UI – as a matter of fact, ooNaThing and ooNaBeing should be abstract classes, and I’ll change that just not right now. Anyway, when we get to that point we should create the class through the following selection in Visual Studio. There’s probably an equivalent in Blend, but I don’t know what it is. The reason you should do this is it creates generic.xaml for you and the style resource that wraps around your ControlTemplate, including a ControlTemplate skeleton for you to fill in.

image

And it generates the code in the Seeker constructor that sets this.DefaultStyleKey. This causes the default style for seeker to be the “ooNa:Seeker” type in generic.xaml. Notice that you have to change the inheritance from Control to your base class, in this case ooNaBeing.

namespace TemplatedSeeker.ooNaClasses
{
    public class Seeker : ooNaBeing
    {
        public Seeker()
        {
            this.DefaultStyleKey = typeof(Seeker);
        }
    }
}

And here’s generic.xaml. It turns out the seeker actually is just another pretty ooNaFace. Except for living off his ancestors, all he’s got going on is in generic.xaml.

You can see that the ControlTemplate is wrapped in a Style, and the TargetType for both is “ooNa:Seeker”. As far as I can tell, you can only put one Style/ControlTemplate  per class in generic.xaml. By definition it is the default style for that class. Note that’s what it’s designated as in blend, ‘Seeker default’ – I didn’t give it that name. I just set up the Style/ControlTemplate you see below.

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:ooNa="clr-namespace:TemplatedSeeker.ooNaClasses">
    <Style TargetType="ooNa:Seeker">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="ooNa:Seeker">
                    <Grid>
                        <Ellipse Fill="Red" 
                                 x:Name="VisualMe" 
                                 Stroke="Black" 
                                 Height="60" 
                                 Width="40"
                                 HorizontalAlignment="Left" 
                                 VerticalAlignment="Top" 
            />                 
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

You still have to add the UI to generic.xaml – you can do it through Blend, I think I recall but I’m not going to get into that here. Poke around in the menu on the right hand side when you have generic.xaml selected and go for it.

Working with UserControl and the associated xaml file, then generic.xaml with classes that inherit from Control, then with new style resources to modify the appearance of those classes you’ll see that ControlTemplate, with just a little tweaking by the programmer,  leverages UserControl so the UI can be managed by the designer; it’s UserControl with a UI that can be swapped-out at will. That’s pretty huge.

I threw in a MouseLeftButtonDown event handler to move both seekers to the click point just to prove that the Position property in ooNaThing is working.

Next time I’ll get into visual states and hopefully the whole “parts and states” gestalt.

Download code:

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





Comments are closed.