Ah, we’ve made it to Day #27 of the 31 Days of Silverlight. Today, we’re going to be decontructing one of the most common XAML elements: the Button. The reason we’re doing this is to demonstrate how templates work, and how even complex controls are really just compositions of much simpler ones.
1. Add a button to your XAML page
Button, button, who’s got the button? Just add a simple button to your page. If you need help, you can use this XAML:
<Button Content="Click Me!" Width="200" Height="200" />
2. Open your solution in Expression Blend 3
If you haven’t already installed Expression Blend 3, you can get a 60 day trial here.
Open MainPage.xaml, and you should see your button in the middle of the design surface. If you right click on the button, you should see a menu option: Edit Template. From there we’re going to choose “Create Empty…”
3. Creating a style resource
When you choose that menu item, you are presented with the “Create ControlTemplate Resource” dialog box. Here, you can name your new Template, as well as define its scope. For my example, I am choosing Application level scope, so that I can use this across all of my pages. What you will see is that you have added a big style-type definition to your App.xaml file. (For more info on styling, you can check out Day #10: Styling Silverlight Controls.)
4. Looking at our template
If you look at the “Objects and Timeline” panel in Expression Blend, you’ll see very little in the way of structure. The only thing that is provided is a Grid. If you open your “States” window, you’ll find that a button has several different states. This is where we will be spending most of our time.
5. Adding an image to our template
I called this Template an ImageButton, because I am going to create a button that has a consistent image in the background. So, to add an image, just double-click on the Image control in your toolbox.
I recommend stretching it to the size of your template…that way, your button will be proportional when you create other sizes in your application. Also, you need to add an image to your project that you’re going to use. For my example, I’m using a picture of my face. Think of it as a public service for you to hit me in the face over and over. 🙂
6. We also need a ContentPresenter
We need to add a ContentPresenter control, but that’s what it is. This will show the “Content” property of our buttons. I’m putting this control on my forehead. Here’s what it looks like:
If you go back to Visual Studio and run this project, you should now have a button on your page that instead looks like your image, but still displays your button Content. Give it a try. Mine says “Click Me!” like the initial XAML I gave you.
7. Playing with States
If you open the States window in Expression Blend 3, you’ll see that we already have a bunch of States defined for a button. We’re going to be modifying some of these, so that our ImageButton actually does something. You may have noticed that while our Content showed up appropriately, the button didn’t have that “click” feel we’re used to. This will fix that. Here’s a shot of the States window:
What we are going to do is add some behaviors to the “Pressed” state so that the button reacts when it’s clicked.
8. Adding the Pressed state
In the States window, click on the “Pressed” item. You’ll notice that immediately the design pane is outlined in red. This is because we’ve started recording our actions. The changes you make at this point will be the changes that happen when you “Press” the button. In my example, I am going to have my Image control change its opacity. At this point, we’re just creating a simple animation. Look back to Day #11 in this series to see more about animation. I am going to create an animation that changes the opacity from 100% to 33% in just 0.1 seconds. Here’s what the entire contents of my App.xaml file looks like now:
<Application 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"
x:Class="SilverlightTemplating.App"
mc:Ignorable="d"
>
<Application.Resources>
<ControlTemplate x:Key="ImageButton" TargetType="Button">
<Grid Height="200" Width="200">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Pressed">
<Storyboard>
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="image" Storyboard.TargetProperty="(UIElement.Opacity)">
<EasingDoubleKeyFrame KeyTime="00:00:00" Value="1"/>
<EasingDoubleKeyFrame KeyTime="00:00:00.1000000" Value="0.33"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Image x:Name="image" Source="jeffblankenburgheadshot.jpg" Width="200" Height="200"/>
<ContentPresenter Height="24" VerticalAlignment="Top" Margin="53,60,53,0"/>
</Grid>
</ControlTemplate>
</Application.Resources>
</Application>
You may notice, when you run this project, that when you click on the button, it goes transparent, but when you release the mouse, it doesn’t “un-transparentize.” We need to tell it to do that as well. This is a completely open slate we’re working with, and nothing is assumed in a template. This is a plus, however, because although we are working in blank template, if we like 90% of a certain control, we can use that as our starting point. Let’s add the “MouseOver” state now.
9. Adding the “MouseOver” state
Since we want the button to return to its original state when we lift the mouse button, we also create a MouseOver state. This is the state that the control will revert to when we lift the mouse button. I actually copied and pasted the XAML from my Pressed state into this new MouseOver state, and changed the opacity values. Here’s my new XAML:
<Application 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:ic="clr-namespace:Microsoft.Expression.Interactivity.Core;assembly=Microsoft.Expression.Interactions"
x:Class="SilverlightTemplating.App"
>
<Application.Resources>
<ControlTemplate x:Key="ImageButton" TargetType="Button">
<Grid Height="200" Width="200">
<VisualStateManager.CustomVisualStateManager>
<ic:ExtendedVisualStateManager/>
</VisualStateManager.CustomVisualStateManager>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="MouseOver">
<Storyboard>
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Duration="00:00:00.0010000" Storyboard.TargetName="image" Storyboard.TargetProperty="(UIElement.Opacity)">
<EasingDoubleKeyFrame KeyTime="00:00:00" Value="1"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Pressed">
<Storyboard>
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Duration="00:00:00.0010000" Storyboard.TargetName="image" Storyboard.TargetProperty="(UIElement.Opacity)">
<EasingDoubleKeyFrame KeyTime="00:00:00" Value="0.33"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Image x:Name="image" Source="jeffblankenburgheadshot.jpg" Width="200" Height="200"/>
<ContentPresenter Height="24" VerticalAlignment="Top" Margin="53,60,53,0"/>
</Grid>
</ControlTemplate>
</Application.Resources>
</Application>
Summary
In short, this is a robust piece of functionality in Silverlight. Not only can we completely overhaul any of the controls, but we can also use them as templates for future, robust controls that we may imagine tomorrow. If there’s ANYTHING you don’t like about the default controls in Silverlight, you have the power to change them. That is incredibly liberating. Here’s what my new pseudo-button looks like:
Leave a Reply