Click here to Skip to main content
Click here to Skip to main content

Picture Frame Control in Expression Blend & Silverlight

, 9 Jun 2010 CPOL
Rate this:
Please Sign up or sign in to vote.
How to build your first re-useable Control from scratch in Expression Blend.
Click here to Skip to main content

Introduction

Welcome to my third Beginners tutorial for Expression Blend & Silverlight.

(Updated with new additional Style Brushes thanks to Andrew Rissing - See here!)

Apologies to some of you, for the deviation from buttons, but there is so much more to Blend!

img22.jpg

But it is definitly NOT Picture Frames!

Although a Picture Frame will be an excellent oppertunity to build our first Control.

A Control is basically a reusable Asset, with pre-defined Properites & a Style.

ExtraBrushesFrame.jpg

 

Overview

A Developer friend came to me in a bit of a panic, saying he needed a picture frame with "mitred" corners (45 degree's) for a demo he was presenting the next day. He'd had a quick go at creating it himself, but was having problems doing the mitred corners of the frame.

Expect he probably also thought: Why have a dog, & bark yourself? (English Expression)

So I threw something together for him, but as a Designer was concerned about the context in which the picture frame was going to be used. I.E. what colours would be used, how thick would the frame be, & what sort of profile would look best. As such, I wanted to ensure these were all easily editable for him, & the frame I created (& put my name too), would look as good as it could. (We Designers are very sensitive about our work being presented the best it can be - Sorry!)

So whilst making the Picture Frame, I released it would make an excellent beginners tutorial, on making your first Control.

Before commencing this tutorial, I recommend that you read my previous CodeProject tutorials:

"Building Better Buttons" (A beginners guide).

"Arcade Button" (Controlling the position of elements in a Control Template)

I have divided this tutorial into sections for easier reference:

  1. Setting up the basic Frame
  2. Profiling the Frame
  3. Converting into a Control
  4. Cosmetic Surgery & Bindings
  5. Paths & Vector Graphic data
  6. Final Touches
  7. Additional Functionality & Thoughts
  8. Deployment & Re-use
  9. Source

 

Section 1 - Setting up the basic Frame

Create a new Silverlight project in Expression Blend, & call it something like "PictureFrame".

Using the Selection Tool, roughly set up some Grid dividers as shown below.

(Don't worry about the proportions, we will sort these out later).

Now select the Rectangle Tool, & drag out a Rectangle to fill the top left square.

Ensure the Margins are all set to 0, & remove the Stroke.

Set the Fill to a colour that stands out on the background, (I've chosen a bright green).

Right click on the Rectangle, & choose Path > Convert to Path.

(I have also changed the colour of the LayoutRoot Grid, to an off white for the clarity of this tutorial).

Select the Pen Tool from the left hand tool bar, & click on the bottom left point of the Rectangle (Square) to delete it.

(This could also have been achieved by using the Direct Selection tool, selecting the node/point & deleting it).

Select the newly created Triangle, & duplicate it using Copy & Paste.

Now move the duplicated Triangle over to the top right square of the Grid, & ensure the Margins are set to 0.

Next in the Transform section of the Properties tab, Flip the triangle along the X axis

Select the Rectangle Tool again, & drag out a Rectangle to fill the middle section of the Grid between the 2 Triangles.

Ensure the Margins of the Rectangle are set to 0, & the Stroke & Fill match the Triangles.

In the Objects and Timeline, rename the 3 new elements as shown in the image below.

(TopLeftMask, TopRightMask & TopMiddleMask)

Select these 3 elements, & duplicate them by using Copy & Paste.

Move the duplicated elements to the bottom of the Grid, & Flip them along the Y axis to invert them.

Ensure the Margins are set to 0, & rename these elements to BottomLeftMask, BottomRightMask & BottomMiddleMask.

Duplicate more of these elements, & fill in the side pieces as shown below.

Flip the Triangle elements to fit, & resize the Rectangles - Do not Rotate any of the elements!

(I have changed the Fill colour of the side elements to blue for clarity).

Ensure all the Margins are set to 0, & rename the elements to suit their locations in the Grid.

Section 2 - Profiling the Frame

Now we have the basic components of the Frame, let us make it look a bit more appealing.

Select the TopMiddleMask element, & set the Fill to a Linear Gradient as shown below.

(With black Gradients Stops at 0 & 100 on the Ribbon, & a white Gradient Stop at 40 on the Ribbon).

Now in the Advanced Properties for the Fill, select Convert to New Resource.

In the popup window, call this new resource "VerticalGradient".

Now select the rest of the Top & Bottom elements, & apply this newly created resource, to the Fill of each.

Next repeat the process to setup a HorizontalGradient resource for all the side elements.

Depending on how you moved & Flipped your elements, you will probably end up with something looking like this.

Notice that the Gradients on some of the side elements, do not match.

(I purposely offset the white centre of the Gradient, to show & ensure we get all the elements aligned properly).

If your project looks like the above, the easiest way to re-align all the elements is:

To Flip the LeftMiddleMask & RightMiddleMask elements, & reverse the direction of the Gradient Stops of the HorizontalGradient resource. This will unfortunately mean, that the white Gradient Stop is now set at 60 on the Ribbon, and does not match the VerticalGradient resource.

So instead:

I will still Flip the LeftMiddleMask & RightMiddleMask elements, & use the Gradient Tool to make the Arrow point in the opposite direction.

This can also be done:

By opening the Advanced properties of the HorizontalGradient resource, and changing the StartPoint to 1, & the EndPoint to 0.

Now you should have a Frame, with a matching Gradient all the way around.

If you have got in a muddle, just download & open the file below to get back on track.

Download GradientFrame.zip - 65.61 KB

 

Section 3 - Converting into a Control

What I really want, is to add some colour to this Picture Frame, but I don't want to set this in the Gradients. As I will have to adjust the Gradient Stops individually, which is fiddly & time consuming. Especially if I add more detail to the Picture Frame, by adding more Gradient Stops.

Instead I want to control the colour of our Picture Frame in a single place. Just like we have done in the "Style" for my previous tutorials, (Building Better Buttons & Arcade Button)

But at the moment we do not have a Style, just a collection of element. So we need to group these elements in someway, so that they are all packaged up in a Template that has a Style!

This is really easy, & all we need to consider is what sort of Control Template best fits our Picture Frame. Now if you have looked at my previous tutorials, we have played around with a Button Control, its Style, & its Template.

For simplicity, & because those tutorials were for beginners, I referred to the button as a UserControl, it is not! A button is a Control, and can be considered as part of a UserControl - That is all you need to know for now!

When we make a Control, we need to base it on an existing Control, so that Blend can give is some basic Properties. The Picture Frame is obviously not a button, and as such, does not need to have all the functionality that a button does. So even thought I could base this Picture Frame on a button Control, it would not be appropriate.

Instead, we should look for the most basic Control that meets the needs of our Picture Frame. For this, I have chosen to use the "ContentControl" as the basis for our Picture Frame.

So in the Objects and Timeline right click on the LayoutRoot Grid, & choose Make into Control.

In the Search area of the popup window, type "content" to filter all the available Controls.

Then select the "ContentControl" in the results window.

In the Name (Key) field, call your new Control something like "PictureFrameControl", & hit OK.

Note: have purposely avoided calling it "FrameControl". as Blend already has a Control called "Frame"

(The FrameControl is a ContentControl with added navigation, which is not needed here)

You can call your Control anything you like, but clarity for all who may use ore reference it, should be your primary concern!

Hopefully you will have noticed that in the Objects and Timeline, we are now in the Template for our new Control. The top left corner of the Artboard has also changed, to show we are in the Template of our PictureFrameControl. We also now have a ContentPresenter as part of our Control, as this is a default component for a ContentControl.

In the Artboard, the ContentPresenter is currently obscured, as it is in the top left corner of our Control.

So select the ContentPresenter, & in the Artboard, drag it into the centre section of Grid.

Reset the Margins, so that the ContentPresenter sits in the top left corner of the centre section of the Grid.

And that is it, with regards to making your own Control!

 

Section 4 - Cosmetic Surgery & Bindings

Now we have a Style, can think about adding some colour to our Control, as well as controlling the thickness of our Picture Frame.

So firstly, go to the Style for our PictureFrameControl, & set a nice colour for the Fill. (I have chosen Blue)

Now back in the Template of our Control, select the Rectangle Tool from the left hand tool bar.

And in the Artboard, drag out a Rectangle to fill all 3 sections of the top row, as shown below.

Note: I have temporarily set the Fill of this Rectangle to Red for the clarity of this screenshot.

Rename this Rectangle to "TopBGround", & remove the Stroke.

Now set the Fill to Template Binding > BorderBrush.

Repeat this for the bottom row, & both side columns, naming them "BottomBGround", "LeftBGround" & "RightBGround".

In the Objects and Timeline, move these 4 new elements to the top of the list.

So that they are behind all the Gradient (Mask) elements.

Now in the Objects and Timeline select all the Gradient (Mask) elements, & set the Opacity to 50%.

With a bit of luck, your Picture Frame should look something like the image below.

Now this is all very pretty, but the Frame is rather chunky, so let us address that next. There is nothing wrong with us adjusting the Frame thickness, by adjusting the Row & Column dividers, that we set right at the start. But we can do a lot lot better than that!!!

How about setting the Frame thickness, so that it is controlled by the Border property of the Style? This would make it easier to control & adjust for our future needs. Sadly in Blend 3, we cannot visually just select the border property that we wish to Bind to (Link to). Instead we need to use a Custom Expression, to Bind to the Top, Bottom, Left & Right of the Border property of the Style.

So select the TopBGround element, & in the Advanced Properties of the Height, select Custom Expression.

In the popup window enter: {Binding BorderThickness.Top, RelativeSource={RelativeSource TemplatedParent}}

(Don't worry about understanding the notation of this Expression, just notice that we are Binding to the BorderThickness.Top).

Repeat the process for the Height of the BottomBGround element, but change BorderThickness.Top, to BorderThickness.Bottom.

Now set the Width of the LeftBGround & RightBGround elements, to BorderThickness.Left & BorderThickness.Right respectively.

The Blue background of the Picture Frame has disappeared, & this is because the Border Thickness of the Style is set to 0.

So go to the Style, & set the Border Thickness to 50 on all sides.

This should give you a Picture Frame looking something like the image below.

Not quite what we want yet, even though it is an interesting looking image.

We want our Blue background elements, to grow from the outside towards the centre.

So back in the Template, select the TopBGround element, & set the VerticalAlignment to Top.

Next select the BottomBGround element, & set the VerticalAlignment to Bottom.

Now set the LeftBGround & RightBGround elements, to a HorizontalAlignment of Left & Right respectively.

This should now make your Picture Frame look like the image below.

(Interesting, but not yet quite what we want).

The Grid dividers are currently set to Star (Unlocked Padlock), which means they are proportional to the overall scale.

And if you have read my Arcade Button tutorial, you may remember that I said there were 3 states for these Grid dividers.

Star, Fixed & Auto-sized.

We want Auto-sized, so that the Grid divider will grow or shrink, depending on the needs of the elements within that portion/division.

So click twice on the 1st & 3rd Row & Column dividers, to change them to Auto-sized and match the image below.

Very little (if anything) has changed, & this is because Blend has automatically applied a Minimum value for these Row & Column sizes.

This can be very handy at times, but a bit of a pain for our needs...

To remove these Minimum settings, we need to either go to the Row & Column Definitions of the LayoutRoot element.

These can be found in the Advanced part of the Layout section, & the Advanced part of the Definitions Editor.

(I have shrunk this window down for this tutorial).

But it is a lot easier to select the LayoutRoot element, & edit the XAML directly.

Delete the MinWidth & MinHeight parameters applied to the Column & Row Definitions.

So that your XAML looks like the image below.

While we are here looking at the XAML, it is worth noting:

That the "Control" Template for this PictureFrame "Control", is based on a TargetType of "ContentControl".

But back to sorting out the Frame Thickness...

Now even though we have removed these Minimum setting for our Column & Row sizes, nothing has changed in the Artboard.

The Thickness of our Picture Frame has remained unchanged, and the reason why seems unclear...

Hmmmm.... Smile

 

Section 5 - Paths & Vector Graphic data

From the title of this section, you have probably guessed what is preventing our Frame thickness from resizing. It is the Path elements that are stopping our Column & Row dividers, from resizing to fit the Border parameters of the Style. So let us have a look at these, and how they work.

Anyone who is familiar to Vector Graphics, will know that all the points or lines that make up a Vector Graphic are relative to one another. But you have probably never really cared, that these have a default size or scale. When you open a Vector Graphic, it will size based on the units described within that file. And Blend does exactly the same, so let us look at this data.

Select one of the Path elements, like the TopLeftMask element, & look at the XAML code.

In the image below, I grabbed the snippet we are interested in.

Look at: "Data="M0,0 L120,0 L120,120 z"

What does this all mean?

Well, basically from a starting point of "0,0" in the top left corner, extend a distance of "120" along the X axis, & "0" along the Y axis. After defining this point, define the next point from the same starting point, at "120" along the X axis, & "120" along the Y axis.

This defines 3 points if you include the start point, which is all that is needed to define a triangle. These distances are defined in Blend as pixels, and this specifies the default size of the Path that makes up our Triangle corners.

As such, Blend see's these as the Minimum sizes that these elements should be displayed or Drawn at. So it is the Paths, that are superficially preventing our Column & Row dividers, from following the Border thicknesses we are setting in the Style.

In this example, we do not care what Scale these Triangles are drawn at, just that they are triangles & proportionally correct. So we could set the first point at 1 pixel along the X axis, & the second point at 1 pixel on the X axis & 1 pixel along the Y axis. Combined with our starting point, we have 3 points, and our triangle proportionally correct.

But this would mean, the Minimum size of our Border (Frame Thickness) would be 1 pixel. Not ideal, if we want 3 or less sides to our Picture Frame. Instead it would make more sense, if we set the Scale of our Triangle to 0.1 of a pixel, as this would equate to 0 when rendered in pixels. The proportions would be the same, & this is all we care about in our Picture Frame.

So in the XAML, select "Data="M0,0 L120,0 L120,120"", and using the Replace feature from the Edit menu.

Change to "Data="M0,0 L0.1,0 L0.1,0.1 z"", & Replace All, as shown in the image below.

As if by magic, the Picture Frame Thickness updates in the Artboard, to look like the image below.

You now have a fully functional Picture Frame Control, that needs just a few touches to tidy it up.

But feel free to come out of the Template & play around with the Border Thickness of the Control.

 

Section 6 - Final Touches

In the Template, select the LayoutRoot element, & Template Bind it to the "Background".

Select the ContentPresenter, & Template Bind it to the Padding of the Style.

In the Style, set the Background & BorderBrush colours to suit your taste.

(I would suggest you set the Background to have "No Brush" (Transparent).

If you desire, apply some Padding to space the ContentPresenter from the edge of the Frame.

And that is about it!

 

Section 7 - Additional Functionality

Hopefully you realise, that the Horizontal & Vertical Gradients can be adjusted to change the Frame profile. As well as the Alpha values of the Gradient Stops, & the element Opacity's as a whole. But before you start playing & adjusting these, I suggest you make a copy of the Picture Frame "Style". This way you can have multiple Picture Frame "Styles", each with a different Frame profile.

To make a copy, come out of the Template & Style using "Return Scope".

Select the Picture Frame, & from the Objects menu, select Edit Style > Edit a Copy.

This will leave all the parameters you set for the previous Style as they were. And any updates to the Style you make now, will only apply in the new Style. This however, will not apply to the Horizontal & Vertical Gradients, as they are not contained within the Style. These Resources are part of the UserControl, which in simple terms, is your whole page. So if change these Resources, the updates will be applied to both the Picture Frame "Styles", as they both reference them.

The obvious easy solution, would be to duplicate these Resources, and then rename & re-reference them, for one of the Styles. You may also be thinking that, it would be really cool if we could add additional Brushes to the Style. As then the Horizontal & Vertical Gradients, would be wrapped up nicely in the Style. And I wish it was just that simple, but it requires the help of a Developer, to get more brushes in the Style. What they would essentially do, is inherit from the ContentControl & define additional Functionality.

Making it a CustomContentControl, & not a Style we can drop on any old ContentControl.

So you would have to judge, is it really worth it?

Well it was to Andrew Rissing!!! Who kindly stepped in & added these Styles to the PictureFrameControl (CustomContentControl) in the form of "Attached Properties". We had a few minor issues getting it to initialise correctly in Blend, so ensure you Rebuild (Or possible Run, Close & Re-Open) if you experience any initial issues. But I'm fairly sure it is all OK! Smile | :)

ExtraBrushesFrame.jpg 

ExtraBrushesResources.jpg

Thanks very much Andrew, Ur a Star!!! And you can find Andrew Rissing's: Attached Dependency Properties" (Article/Tip) here.

(More discussion on these Extra Brushes can be found in the "Conjecture" section, later in this tutorial)

There is another thing to consider, when I previously chose the ContentControl, as the basis for my Control. A ContentControl is a Base Class, and other Controls, like the FrameControl inherit from the ContentControl. (Imagine that a Base Class is the foundation, and Controls build on this foundation, additional functionality to the existing functionality). Meaning that most things that inherit from the ContentControl, will be able to have our PictureFrameControl Style applied to it. Put a Frame Control in the LayoutRoot (On the Page), & see if you can apply the Style we have created for the ContentControl.

Good isn't it?

But if I had based my PictureFrameControl on a FrameControl, I may not have been able to property apply the Style to a ContentControl. As the FrameControl may have additional functionality defined in the Style, that is just not in the ContentControl Style. Probably not a problem in this example, but it can be, & why I chose to base my Control on the Base Class.

Something else this allows, which I will describe in very simple terms to convey the concept is:

By placing the Style in the Base Class, we have that Style, & the Style of the inherited object (FrameControl for example) to play with. So we can set the Style in the Base Class, & override some of those Style properties in the inherited objects Style. (Great if all your objects/Controls share a similar "Theme" or Style, that can be tweaked at a higher level for individual types of Controls).

(Hope that makes sense, & not too deep!)

 

Conjecture: Modified from original article (before Andrew Rissing created extra brushes).

Consider this: How many times are you going to be reaching for the most funky Picture Frame Control on the market? I would only suggest it, if you really needed this functionality, like maybe an application for a Picture Gallery.

There is also the situation/problem of these Brushes "Attached Properties" appearing on every Control, & all over your application. Which may be confusing to someone editing the application later. How will they know it is only the PictureFrameControl that will support these brushes?

While the extra brushes are great, I would probably not use them unless I really needed lots & lots of Styles of this Control. I don't know for certain how these extra brushes will react in a full scale project. So if you have only just a few gradients in you application, I would personally use Resources, just to be safe. As reducing any risk of breakage is a good thing, & maybe an overhead worth taking. And with the greatest of respect to Andrew, I would recommend any designer speak to their developer, before implementing the advanced (ExtraBrushes). But as a design tool, the advanced Control is excellent, & allows for really easy manipulation of the frame contour & colour hints.

Now the very last thing is to consider with these Gradient Resources, is light direction. Is the light coming from the standard 315 degree's (10:30 on a clock face)? If so, then we actually need 4 Gradient Resources for each Frame Style. As the Top & Bottom elements will need a highlight on opposite sides of the Gradient, & so will the side Gradients.

And before we jump at 4 extra Brushes, we could think about setting up a whole new set of Overlays (Masks) on top of the existing overlays. (Just so we don't affect the existing Resources). But depending on the profile we set in the existing Resources, it will change the profile point of the highlight or lowlights required in the additional Overlay.

So we are stuck with 4 Gradient Resources, if we want an angled light source.

 

Section 8 - Deployment & Reuse

Now there is nothing wrong, with keeping this PictureFrameControl where it is, sat in this project. We could just Copy & Paste it in to another project when we need it. But it would make a lot more sense, to store it away as a Resource of its own, by placing it in a Resource Dictionary. As then, when we want this type of Control, we just add the relevant Resource Dictionary to the project files. This is very simple, but it is all easy when you know how!

In the File menu select New Item, and a popup window appears, as shown in the image below.

Select Resource Dictionary, call it "PictureFrames.xaml" & hit OK.

Now go to the Resources tab on the right, & ensure everything is expanded.

Now select the "PictureFrameControl" Style, & drag it on to the PictureFrames.xaml.

The Gradient Resources automatically follow, as they are referenced by the Style.

You should now be able to delete the Style & Resources in the UserControl, using right click Delete.

Look in the Project tab, & notice you now have a file called "PictureFrames.xaml".

This is where your Control is now stored, & referenced by the project.

So if you want to add this Control to another project, just go to the Project menu, & select Add Existing Item.

Find your PictureFrames.xaml, which is inside your Project/PictureFrame/PictureFrame directory.

Your Control will now be available in any new project that uses it, as an Asset, available in the Assets menu.

(It was actually available here before we moved it, but I didn't think to mention it).

Hopefully you realise, that we can do the same with the previous 2 button tutorials as well.

So that they are packaged up in a Resource Dictionary, entitled something like Buttons.xaml.

The last thing to mention about Resource Dictionaries, is editting your Controls! Without going into depth, it is a lot easier to edit a Control Template when it is in the UserControl, rather than in a Resource Dictionary! (More on that later...)

That will do for now, hope that was useful, & please rate my tutorials!

Your comments & support means a lot to all us article/tutorial writers!

As well as feedback on any errors, or points that need further clarification...

Cheers,

Alan

Section 9 - Source

Completed PictureFrameControl, with 2 Gradient Resources & Resource Dictionary reference.

Download PictureFrame.zip - 100.02 KB

Just the PictureFrames.xaml (2 Gradient Resources) as a Resource Dictionary.

Download PictureFramesXAML.zip - 1.26 KB

Completed PictureFrameControl, with 2 additional "Style" reference Brushes

Download PictureFrame_ExtraBrushes_.zip - 153.76 KB

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Alan Beasley
User Interface Analyst
United Kingdom United Kingdom
I've been playing with computers since my first Acorn Electron, & after blowing up a few ZX Spectrums. I moved on to the C64 & Amiga, & eventually reluctantly on to the PC.
 
I have learnt a wide set of skills during my 38 years of existence, living in the UK, on the sunny south coast.
 
My main area of expertise is Graphic/Visual Design, Usability & UI Design. I am not a programmer, but am fairly technically minded due to studying Mechanical Engineering at Uni.
 
I have work both Freelance & for IBM as a Graphic Designer, & am skilled in the usual graphics packages like, PhotoShop, CorelDraw or Illustrator, Premier, Dreamweaver, Flash etc.
But I originally started with Lightwave & 3D animation.

Comments and Discussions

 
QuestionBrilliant! A question though... PinmemberGreg Cadmes9-Jul-10 9:01 
AnswerRe: Brilliant! A question though... PinmemberAlan Beasley9-Jul-10 9:19 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.141216.1 | Last Updated 9 Jun 2010
Article Copyright 2010 by Alan Beasley
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid