Sunday, April 25, 2010

A Chrome and Glass Theme - Part 4

[UPDATED]: Fixed the links to the final source.

So back to the series on building a theme. Apart from describing the steps to style each of the controls for this particular theme, I've been trying to describe some of the techniques and knowledge that can be applied generally to any theme or style.
  • In part 1 we covered creating style resources in a resource dictionary.
  • In part 2 we covered editing control templates as part of a style, including states.
  • In part 3 we knocked off a couple of the easier controls to style, although we added some extra elements to the control template.
In this post, we are going to dig down into a control that is made up of a collection of other controls: the ListBox. But we are going to begin by styling the other controls that make up the ListBox. If you haven't been following along, grab the files from the end of part 3 here and load them up in Blend. This is what the final result will look like (with sample data):


Parts of a ListBox
A list box has a number of templates that it uses and it's important to get a good understanding of how it hangs together. Here is the break-down:



A list box has a control template that contains a ScrollViewer control. A ScrollView control has a control template that contains two ScrollBar controls. And the ScrollBar controls have their own template as well (which is divided up into horizontal and vertical elements).

Additionally, the ListBox uses two other templates (there is a third that is not part of the reusable style that we will discuss briefly at the end): another control Template (inside the ListBox.ItemContainerStyle) that determines what the items in the ListBox look like, and an ItemsPanelTemplate (ListBox.ItemsPanel) that determines how the items are laid out (eg horizontally, vertically etc).

When you are looking at a styled ListBox, the items in the list look the way they do because of the ListBox.ItemContainerStyle template, which is a ControlTemplate. They are laid out the way they are (usually vertically) because of the ListBox.ItemsPanel which is an ItemsPanelTemplate that contains a stack panel by default, and they are "framed" the way they are (the background, scrollbars etc) because of the ListBox.Template which is a ControlTemplate. We won't bother changing the ListBox.ItemsPanel in this post since we still want the behavior that we get with the default vertical StackPanel.

The ScrollBar
It's easy to get lost in template editing if you dig down and down through the control templates. We are going to approach things in a fairly simple-to-follow way by styling the resources we need from the innermost controls, and work our way back up to the top level control. The first control, buried deep inside the ListBox control, is a ScrollBar. Strangely enough, the ScrollBar is probably the most time consuming of the controls we will style even though it seems a simple control. You'll see why as we go along.

Drag a scroll bar onto our glassy panel - it will be vertical by default. Use the breadcrumb toolbar to edit a copy of the control tempate; call it ScrollBarChromeStyle and remember to save it in our ChromeGlass.xaml resource dictionary.

The template doesn't have any size defined, but Blend allows us to define a Design-Time only size. Select the "Root" element in the Object tree and use the bottom blue drag handle to make our scroll bar a bit taller.

If you expand the "VerticalRoot" element you will see that the scroll bar is made up from three rectangles and five controls. We are only going to need two of those rectangles, so delete the second one in the Object tree under the "VerticalRoot" grid. Set the Fill of the first Rectangle to our GlassFill resource brush, and the stroke of the second Rectangle to the "ChromeBorder" resource brush we defined in Part 1.

Select the "VerticalSmallDecrease" element - it's a RepeatButton control, as are the other small and large decrease buttons. Use the breadcrumb toolbar to edit the current control template. The Repeat button is a specialized kind of button that does not contain any content of its own (like a regular Button does), but still has button states. Select the "Background" rectangle element, change its Fill brush to the "ChromeGlass" resource brush, it's Stroke to empty, and its Opacity to 100%.

Select the "BackgroundMouseOver" Rectangle element, change its Fill to empty, and its Stroke brush to the "ChromeBorder" resource brush.

Select the "BackgroundGradient" Rectangle element, drag it up to be above the "BackgroundPressed" Rectangle in the Object tree, and change to following properties (don't worry about any warnings of animations being removed):
  • Set its Fill to the "ChromeFill" resource brush
  • Set its Stroke to the "ChromeBorder" resource brush
  • Set its Margin to 2 for all edges

Select the "BackgroundPressed" Rectangle element and change its Margin values to 2 for all edges. Select the "Highlight" Rectangle element and change its Margin to 0 for all edges.

Finally, select the unnamed path element and change it's foreground to White. Now to set the values for the different states.

Select the "MouseOver" state (in the "States" tab) and change the Opacity of the "Background" Rectangle to 0%. Select the "Pressed" state and set the Opacity for the "Background" element to 0%, the "BackgroundGradient" element to 100%, the "BackgroundPressed" element to 20%, and the "Highlight" element to 20%.

The good news is, that the VerticalSmallDecrease element is now styled. The bad news is, that we have to repeat this process for the VerticalSmallIncrease the HorizontalSmallDecrease, and the HorizontalSmallIncrease. Since the VerticalSmallIncrease element is essentially the same as the VerticalSmallDecrease element, I won't bother repeating the same instructions. Go ahead and follow the same instructions for the VerticalSmallIncrease RepeatButton; I'll wait for you...

...and we're back. Now lets style the "VerticalThumb" element. Select it and use the breadcrumb toolbar to edit the current template. You should see it's made up of nothing more than five Rectangle elements. Here we go (you may wish to select the top Grid element and use the Design Time size handles so you can see what the changes look like):

Change the "Background" element's Fill brush to empty, and its Stroke to the "ChromeBorder" resource.

Move the "BackgroundGradient" element up under the "Background" element, change its Fill to the "ChromeFill" resource and its Stroke to the "ChromeBorder" resource, and set its margin to 2 for all edges.

Change the "BackgroundMouseOver" element's Fill to White, its stroke to empty, and it's Margin to 2 for all edges.

Change the "Highlight" element's Margin to 0 for all edges.

Select the "MouseOver" state, and change the "BackgroundMouseOver" element's Opacity to 20%. Select the "Pressed" state and change the "BackgroundPressed" element's Opacity to 30% and the "Highlight" element's Opacity to 30%.

That's the vertical layout elements styled; now we have to do the horizontal layout elements. Normally, the easiest way to style the horizontal layout would be to click the "[scrollbar]" button on the breadcrumb trail and change the Orientation of the ScrollBar to Horizontal (you will want to resize it too), and then use the breadcrumb trail to go back into the template and expand the "HorizontalRoot" grid element.

However, at this point Blend seems determined to make things hard for us; it insists on only showing the design template for the vertical orientation regardless of the value of the Orientation property. So we are going to have to do this blind, but we will be guided by what we have done with the vertical scrollbar. You can also switch back and forth between "ChromeGlass.xaml" and "MainPage.xaml" to see the results of your changes. If we had chosen to save the template in "MainPage.xaml" instead of our resource dictionary then we would have been editing "in-place" and would be able to see our changes reflected in the design surface. We probably should have done that, and then moved the template into the resource dictionary when we had finished.

Here are the steps for the "HorizontalRoot" template:

Horizontal Root
  • Delete the second Rectangle element - we don't need it, there should be three left.

  • Set the Fill brush for the first Rectangle to the "GlassFill" resource brush.


HorizontalSmallDecrease
  • Select the "HorizontalSmallDecrease" element and use the breadcrumb trail to edit it's template.
  • Select the "Background" Rectangle element and set its Fill to the "ChromeGlassFill" resource brush, its stoke to "No brush", and its Opacity to 100%.
  • Select the "BackgroundMouseOver" Rectangle element and set its Fill to "No brush" and it's Stroke to the "ChromeBorder" resource brush.
  • Select the "BackgroundPressed" Rectangle element and set its Margin to 2 for all edges.
  • Select the "BackgroundGradient" Rectangle, drag it up in the Objects tree to be above the BackgroundPressed Rectangle, set its Fill to the "ChromeFill" resource brush, its Stroke to the "ChromeBorder" resource brush, and it's Margin to 2 for all edges.
  • Select the "MouseOver" state and set the Opacity of the "Background" Rectangle to 0%, the "BackgroundMouseOver" Rectangle to 100%, and the "BackgroundGradient" Rectangle to 100%.
  • Select the "Pressed" state and set the Opacity of the "Background" Rectangle to 0%, the "BackgroundGradient" Rectangle to 100%, the "BackgroundPressed" Rectangle to 20%, and the "Highlight" Rectangle to 30%.
  • Select the "Highlight" Rectangle element and set its Margin to 0 for all edges.
  • Select the "[Path]" element and set it's Fill to a solid white color.


HorizontalSmallIncrease
Use the breadcrumb trail to go back out to the scrollbar template, select the "HorizontalSmallIncrease" element. For some strange reason, this one template is implemented differently by default than the other 3 small increase RepeatButton elements in the ScrollBar template. There is no "BackgroundPressed" Rectangle element, so select the "BackgroundGradient" Rectangle, press Ctrl+C followed by Ctrl+V to create a copy, select the copy, click its name once and rename it to "BackgroundPressed". Drag it up so it sits just below the "BackgroundGradient" Rectangle, and set its Fill to a solid color with the value "#FF448DCA", its stroke to "#00000000", and its margin to 2 for all edges. Also change the name of the "BackgroundAnimation" Rectangle to "BackgroundMouseOver". You can now follow the same steps as for the "HorizontalSmallDecrease" element, with one additional step: in the "Pressed" state, you will have to set the Opacity for the (now named) "BackgroundMouseOver" element to 0%.

HorizontalThumb
You can follow the same steps for the "VerticalThumb" element to style the "HorizontalThumb".



That's the hard work done, the rest of the ListBox template styling goes quickly now.

The ScrollViewer
Go back out to MainPage.xaml design surface and place a ScrollViewer control inside the grid containing our other controls. The ScrollViewer isn't available on the control toolbar fly-outs so you can either use the ">>" chevron at the bottom of the toolbar to list all controls and find the ScrollViewer in there, or you can select the "Assets" tab and enter "scroll" into the search box and Blend will filter all available controls that contain the search term:


Once you have created the scroll viewer, use the breadcrumb trail to edit a copy of its template, saving it as "ScrollViewerChromeStyle" in the "ChromeGlass.xaml" resource dictionary.

The only thing we need to change in the template is the style of the scroll bars. We can do that by right-clicking the VerticalScrollBar element, and selecting "Edit Template" | "Apply Resource" | "ScrollBarChromeStyle". Do the same to the HorizontalScrollBar. Now select the "[Rectangle]" element and delete it. That's it for the template, we just need to set the border brush on the style resource.

Click the artist's palette on the breadcrumb toolbar to edit the style of the ScrollViewer and change it's Border to the "ChromeBorder" resource brush. We also want to have the inside of the ScrollViewer subtly differentiated from the outside of it so set its Background brush to a "#26000000" Solid brush. The style may show the foreground as Black, but since we have applied a White Foreground to our UserControl, the foreground will default to White when we actually use the control in our MainPage.xaml.

That's the ScrollViewer done. Here is what it looks like on our main page, with a couple of Scrollbars too:


We could have used the "GlassFill" resource brush for the background of the scrollable area, but the ScrollViewer is going to be used in the ListBox template, and the angled shine of the Glassfill brush would end up looking odd with multiple ListBox elements of different sizes on the same screen, since the shine would be at different angles depending on the size of the ListBox.

The Listbox Control Template
Finally, we get to the ListBox control. There are two templates we need to edit here, the control template for the ListBox, and the ListBoxItem template that will hold each item in the ListBox.

You can delete the ScrollViewer control on the MainPage.xaml screen and replace it with a ListBox control. Create a new resource template for the ListBox called "ListBoxChromeStyle" in the "ChromeGlass" resource dictionary.

Expand the template in the Objects tree and right click the "ScrollViewer" element. Select "Edit Template" | "Apply Resource" | "ScrollViewerChromeStyle". You will see that the background is still white from the default template because the Background brush is template-bound to the parent Background brush for the ListBox control. We could reset the Background brush for the ScrollViewer, but that would mean that any changes we make to the Background brush would be ignored for individual ListBox controls that use this style. So select the artists palette on the Breadcrumb toolbar for the actual Style of the ListBox and set the Background to a solid color of "#22000000" - the same that we used for the ScrollViewer style template. The Border brush of the "[Border]" element in the ListBox template is also template-bound to the style, so change the style's BorderBrush to the "ChromeBorder" resource brush. The Foreground color of the parent control is not passed onto child ListBoxItems, so set the Foreground of the ListBox style to white.

That's the main chrome of the ListBox done, so now lets do the ItemContainerStyle.

The ItemContainerStyle
Go back out to the MainPage.xaml view and select the ListBox. Use the breadcrumb trail to edit, under "Edit Additional Templates", a new copy of the "Generated Item Container". Call it "ListBoxItemChromeStyle" and save it in the resource dictionary.

Select the "fillColor" Rectangle element and change its Fill the the "ChromeGlassFill" resource brush. Select the "fillColor2" Rectangle element and change its Fill also to the "ChromeGlassFill" resource brush, and it's Stroke to the "ChromeBorder" resource brush. Select all three Rectangle elements by holding Ctrl and clicking on each one, and change the RadiusX and RadiusY values to 3.

Now select the "MouseOver" State, and set the Opacity of the "fillColor" Rectangle to 50%. Select the "Focused" State and set the Opacity of the "FocusVisualElement" Rectangle to 30%. Select the "Selected" State and set the Opacity of the "fillColor2" Rectangle to 100%. Finally, select the "Disabled" State, delete the Opacity adjustment for the "contentPresenter" element and set the Opacity of the "grid" element to 55%.

One thing that has always annoyed me about the list box is that the default template for the ListBoxItem has the HorizontalAlignment set to Left - which means that the selected item highlight doesn't stretch across the whole ListBox. To change this, select the "contentPresenter" element and use the Advanced Property Options box to reset it's HorizontalAlignment, and then change it to Stretch instead of Left.

That was the last part of the reusable ListBox template that we needed to style, but there is one more thing to do. Currently, the ListBox control that we created has its "ItemContainerStyle" property set to the "ListBoxItemChromeStyle" resource that we just created. But we really want this to be set on the "ListBoxChromeStyle" style resource rather than on the ListBox itself. So select the ListBox, and from the "Object" menu, select "Edit Style" | "Edit Current" to edit the "ListBoxChromeStyle" resource. Expand the "Miscellaneous" group heading and, from the advanced property options box on the right hand side of the "ItemContainerStyle" property, select the local resource "ListBoxItemChromeStyle". Finally, exit out of editing the template and, back on the ListBox itself, use the advanced property options to reset the value of the ListBox's "ItemContainerStyle" property. So now, whenever we use our "ListBoxChromeStyle" resource, it comes already connected to the "ListBoxItemChromeStyle" resource.

That is the last thing we needed to do to our ListBox style, so now lets add some sample data to test it with.

Adding Sample Data
Blend has built in functionality to create sample data that we can use when designing our layouts to test how those layouts work with actual data.


Select the "Data" tab and click the "Add sample data source" button. Select the "Define New Sample Data..." option and accept the default Data source name "SampleDataSource", creating it in the Project, not the Document.

Blend will have created two fields for you by default. You can use the little arrow on the right edge of the property to change what kind of sample data is generated. Rename the two fields "Name" and "Company" and add a third one named "Image". Use the "Change Property type" arrows to select the right kind of data to be generated for each field.

To bind the generated data to your ListBox, click and drag the "Collection" item in the SampleDataSource tree onto the ListBox. Blend will automatically create a data template for it and show the data stacked vertically in a StackPanel control, but we're going to change that now.

Editing the ItemTemplate
The last template is the Listbox.ItemTemplate. It is a DataTemplate that will be applied to the data inside each ListItemBox. It has nothing to do with our reusable "ListBoxChromeStyle" resource, and is something that you customize specifically for each ListBox you use. I'm covering it briefly here just for the sake of putting all the ListBox templates in context.

Use the Breadcrumb toolbar to edit the current "Generated Items (ItemTemplate)". I'm not going to give any instructions on what to do here since it is unrelated to our style sheet. Just find a nice layout for the items that are there, or grab my one using the link at the end of this post. You will notice that you are editing this template "in-place" since it was created inside the MainPage.xaml file, rather than the resource dictionary.

So here are the things we have covered in this post:
  • It's easier, with complex controls, to style the child controls that it uses first.
  • The different templates that make up the list box and how they relate to each other.
  • How to create reusable styles and templates for each of them.
  • The ListBox.ItemTemplate is specific to each instance of a ListBox, so it's not part of our resource dictionary.

You can grab the final solution files here.

Wednesday, April 7, 2010

Changing Data-Templates at run-time from the VM

[Updated – Fixed code samples]
What do you do when you have a list box or data grid that has a collection of items to display - but some items need to be presented differently than others? For example, I may have a collection of company staff members to display in a single ListBox, but the managerial staff need to have extra information displayed (they always do!), and each level is styled slightly differently like this for example:


In this post I'll describe a technique I have used successfully before - I'll stick with the list of staff example above to illustrate the solution, but the class types are going to be a little contrived for the sake of simplicity.

The Data
Lets define a StaffMember class like this:
   1: public enum StaffRoleType { Manager, MiddleManager, LowerManager, Pleb, Contractor }
   2:  
   3: public enum CoffeeType { Espresso, Cappuccino, HotChocolate, Other }
   4:  
   5: public class StaffMemeber  
   6: {
   7:     public string Title { get; set; }
   8:     public string FirstName { get; set; }
   9:     public string LastName { get; set; }
  10:     public StaffRoleType StaffRole { get; set; }
  11:     public CoffeeType CoffeeType { get; set; }
  12:     public Color FavouriteColor { get; set; }
  13:     public string CarType { get; set; }
  14: }

The StaffRoleType will be used to differentiate the managers from the plebs so we can display the really important information for managers like what kind of coffee they drink and what their favourite color is. If this was real code we might have a different class for each role type, and they would all implement the INotifyPropertyChanged interface. But that class will do for demonstration purposes.


Different Data = Different Data Templates

A nice way to solve this problem would be if we could somehow use different data templates to display each type of staff- but do this inside the existing controls like a ListBox. Ideally we could use Blend to design these different data types too. Of course, the ListBox doesn't let us define more than one data template for the ListBoxItem; but rather than try and rewrite the ListBox, we can achieve our goals with a couple of simple additions to the ContentControl:




   1: using System.Collections.ObjectModel;
   2: using System.Linq;
   3: using System.Windows;
   4: using System.Windows.Controls;
   5: public class SelectableContentControl : ContentControl
   6: {
   7:   public static readonly DependencyProperty TemplateNameProperty = DependencyProperty.Register(
   8:      "TemplateName",
   9:      typeof(string),
  10:      typeof(SelectableContentControl),
  11:      new PropertyMetadata(string.Empty, TemplateNameChanged));
  12:   
  13:   private readonly ObservableCollection<DataTemplate> templateCollection = new ObservableCollection<DataTemplate>();
  14:   
  15:   /// <summary>
  16:   /// Gets the collection of templates
  17:   /// </summary>
  18:   public ObservableCollection<DataTemplate> Templates
  19:   {
  20:      get
  21:      {
  22:          return this.templateCollection;
  23:      }
  24:   }
  25:   
  26:   /// <summary>
  27:   /// Gets or sets the name of the template to use
  28:   /// </summary>
  29:   public string TemplateName
  30:   {
  31:      get
  32:      {
  33:          return (string)GetValue(TemplateNameProperty);
  34:      }
  35:   
  36:      set
  37:      {
  38:          SetValue(TemplateNameProperty, value);
  39:      }
  40:   }
  41:   
  42:   /// <summary>
  43:   /// Select the appropriate DataTemplate when the Content changes.
  44:   /// </summary>
  45:   /// <param name="oldContent">The old Content value.</param>
  46:   /// <param name="newContent">The new Content value.</param>
  47:   protected override void OnContentChanged(object oldContent, object newContent)
  48:   {
  49:      base.OnContentChanged(oldContent, newContent);
  50:      this.SelectTemplate();
  51:   }
  52:   
  53:   private static void TemplateNameChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
  54:   {
  55:      ((SelectableContentControl)d).SelectTemplate();
  56:   }
  57:   
  58:   private void SelectTemplate()
  59:   {
  60:      if (!string.IsNullOrEmpty(this.TemplateName))
  61:      {
  62:          DataTemplate namedTemplate =
  63:              this.Templates.FirstOrDefault(
  64:                  t => t.GetValue(FrameworkElement.NameProperty).Equals(this.TemplateName));
  65:          if (null != namedTemplate)
  66:          {
  67:              this.ContentTemplate = namedTemplate;
  68:              return;
  69:          }
  70:      }
  71:   
  72:      // default to the first template
  73:      if (this.Templates.Count > 0)
  74:      {
  75:          this.ContentTemplate = this.Templates[0];
  76:      }
  77:   }
  78: }

 

The new control has two key properties:



  • Templates - an ObservableCollection of DataTemplate
  • TemplateName - a string representing the name of the template to choose.
The Templates property allows us to declare multiple DataTemplates in the XAML, and the TemplateName property allows us to bind to a property on the data to select which template to use. If we had multiple class types in our list, such as a Manager class etc, then we could bind on the ClassName, or even the class itself with a value converter.



This is what the XAML for a ListBox may look like:


   1: <ListBox ItemsSource="{Binding StaffList}" >
   2:     <ListBox.ItemTemplate>
   3:         <ss:SelectableContentControl TemplateName="{Binding StaffRole}" Content="{Binding}">
   4:             <ss:SelectableContentControl.Templates>
   5:  
   6:                 <!-- default for unmatched StaffRoleType values -->
   7:                 <DataTemplate>
   8:                     <local:PlebianUC />
   9:                 </DataTemplate>
  10:  
  11:                 <!-- Contractor template -->
  12:                 <DataTemplate x:Name="Contractor">
  13:                     <local:ContractorUC />
  14:                 </DataTemplate>
  15:  
  16:                 <!-- Contractor template -->
  17:                 <DataTemplate x:Name="MiddleManager">
  18:                     <local:MiddleManagerUC />
  19:                 </DataTemplate>
  20:  
  21:                 <!-- Contractor template -->
  22:                 <DataTemplate x:Name="Manager">
  23:                     <local:ManagerUC />
  24:                 </DataTemplate>
  25:             </ss:SelectableContentControl.Templates>
  26:         </ss:SelectableContentControl>
  27:     </ListBox.ItemTemplate>
  28: </ListBox>



So what's going on here?



The ListBox has it's ItemsSource bound to a property on the DataContext called StaffList, which contains a collection of StaffMember instances. The ListBox.ItemTemplate contains a single SelectableContentControl with its TemplateName property set to "StaffRole" and it's Content bound to the DataContext of the ListBoxItem. At run time when a DataTemplate is being created for each item in the listbox and the item is databound to it, the SelectableContentControl will read the StaffRole property and find a template with the same name as the StaffRole value. The first DataTemplate in the Templates collection will be used for any data items that have a StaffRole value that doesn't match a Template name. We don't even need a value converter in this example, since the StaffRole property will convert to a string without one.



In order to make this technique friendly to Blend you need to create a UserControl for each of the DataTemplates so that the content can be styled in Blend. If you are more comfortable writing XAML than using Blend then you can skip the UserControls and put the content directly in the DataTemplates. Either way, you will still need to specify the DataTemplate collection by hand in XAML since Blend doesn't provide and easy way to edit a collection of DataTemplate instances (at least that I know of)



Feel free to copy the code above and use it in your own projects. Let me know if you find it useful.

A Chrome and Glass Theme - Part 3

In the first two parts I covered how to use Blend to create reusable resources for brushes and styles, and we styled a Border control and a Button control.

In this post we are going to style two more relatively simple controls: a TextBox, and a CheckBox. So start by re-opening your solution from last time (you can grab it here if you haven't been following along). This is what the final styles will look like:


You can grab the solution containing these styles here.

First: Improve the Button
But first, I want to quickly revisit the Button style we created last time - after playing around with it some more, I don't like the way it looks when it's disabled. We didn't change the default disabled state appearance, which grays-out the control by changing the opacity of the "DisabledVisualElement" rectangle. The problem is that when the button is disabled, it actually stands out more than any enabled buttons around it - which it shouldn't.

To fix it, we are going to give it a glassy look when it's disabled.

Select the button we created last time and use the bread crumb trail to edit the control template. Open up the Object tree and select the grid control under the "Background" Border control. Click the Advanced Properties Options button on the right of the Background brush and select "Reset" to get rid of the link to the template background color.

Select the "DisabledVisualElement" and change it's Fill to a linear gradient with the following markers:
  1. #FFFFFFFF Position: 12.7%
  2. #FFCDCDCD Position 32.8%
  3. #FF898989 Position 35.2%
  4. #FFC2C2C2 Position 39.6%
  5. #FFB3B3B3 Position 49.3%
  6. #FFFFFFFF Position 52.4%
  7. #FF252525 Position 55.6%
  8. #FF808080 Position 100%
And click the little down arrow below the brush editor to show the advanced properties of the gradient brush, and change the Opacity of the whole brush (not the rectangle) to 31%. Now convert that to a new resource called "ChromeGlassFill" in our ChromeGlass resource dictionary.

Finally, select the "Disabled" state, set the Opacity of the BackgroundGradient rectangle to 0%, the Opacity of the DisabledVisualElement rectangle to 100%, and the Opacity of the ContentPresenter to 50%. And set the margins of the DisabledVisualElement to 2. Use the Bread Crumb Trail to exit the style template back into the MainPage. The image to the left shows the button now in it's different enabled states.

The TextBox Control
Before we create a TextBox control we have to group our existing button into a Grid since a Border can only contain one control. Right click the button and press Ctrl + G to group it into a Grid control. Blend will have set the margins on the grid to fit exactly around the button, and changed the button horizontal and vertical alignments to Stretch so reset the margins of the new Grid back to 0, make sure the horizontal and vertical alignments are both Stretch, and the Width and Height are both Auto. Of course now our button takes up the entire grid, so change it's width back to 140, and it's height back to 30. And drag it back into place.

Now add a TextBox control to the Grid, make it about the same size as the button. From the Object menu, select "Edit Style" | "Edit Copy..." and save it as "TextBloxGlassyStyle" in our ChromeGlass resource dictionary. Set it's Background brush to our new "ChromeGlassFill" brush, and it's border to the "ChromeBorder" fill. Set the Foreground brush to a white solid color, the CaretBrush to a white solid color, and the SelectionForeground to black.

Now use the Breadcrumb Trail to edit the current template and open up the object tree. Select the self-named "Border" control and reset it's Background brush to be empty - we are going to animate the opacity of the background, but if we do it on the Border then it will also affect it's child controls.

Create two new rectangles inside the Grid called "UnfocusedBackground" and "EditingBackground" positioned as the first children of the grid as shown in the image to the left. Make sure the margins of the rectangles are reset to 0, the horizontal and vertical alignments are set to Stretch, and the Width and Height set to Auto.

Select the UnfocusedBackground rectangle and use the Advanced Property Options button on it's Fill to use Template Binding to the Template's Background property. Set it's stroke to No Brush.

Select the EditingBackground rectangle, set it's Fill to #4CFFFFFF, and it's Opacity to 0%.

Make the following changes to the Focused State:
  • Set the UnfocusedBackground Opacity to 0%
  • Set the EditingBackground Opacity to 100%
  • Set the FocusVisualElement Opacity to 50%
  • Set the FocusVisualElement Margins all to 0
And these changes to the Disabled State:
  • Set the Border Opacity to 75%
  • Set the ContentElement Opacity to 50%
  • Set the DisabledVisualElement Opacity to 0%

That's our TextBox done. Exit the Style Template and run the solution to try it out.


The CheckBox Control
Create a CheckBox control and use the same technique as for the TextBox control to create a style called "CheckBoxGlassyStyle" in our resource dictionary.

While editing the style, set the BorderBrush to our ChromeBorder LinearBrush resource and set the Foreground to White. Now use the Breadcrumb Trail to edit the control template and make the following changes to the controls inside the template (you can ignore the warnings about animations being removed):
  • Set the Background control's Fill to our GlassFill resource
  • Set the BoxMiddleBackground control's Fill to our ChromeFill resource (you will have to Reset it first)
  • Set the BoxMiddle control's Fill to our ChromeGlassFill resource and it's Stroke to our GlassBorder resource
  • Set the CheckIcon control's Fill to white
  • Set the IndeterminateIcon control's Fill to our ChromeFill resource
You can see the benefit of saving our linear gradient brushes as resources - that saves us a lot of time!

Now we just need to update the states:

MouseOver State
  • Set the BackgroundOverlay Opacity to 30%
  • Set the BoxMiddleBackground Opacity to 30%
Pressed State
  • Set the BackgroundOverlay Opacity to 0%
  • Set the BoxMiddleBackground Opacity to 100%
Focused State
  • Set the ContentFocusVisualElement Opacity to 50%
And that's it! Exit the template back into the Main Page and run your solution.

In the sample at the top of the post I've bound the IsEnabled property of the TextBox and Button to the CheckBox so you can see what they look like disabled.

You can grab the solution containing the above styles here.