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

A Style for Round Glassy WPF Buttons

, 5 Jan 2009 CPOL
Rate this:
Please Sign up or sign in to vote.
A style for giving WPF buttons a glassy, Windows Vista-like look.

main.jpg

Introduction

I really liked the look of the round glassy button style in Windows Vista ever since I installed it. The great thing about WPF is that it allows styling virtually any user interface component. After playing a while with Microsoft Expression Blend, I came up with a style that, in my opinion, looks pretty much like the style I was after. So, I thought I might share it. As I said, I've used Microsoft Expression Blend to draw the button, I've also used the great XAML editor - Kaxaml - for some fine tuning.

Overview

The glass button style consists of three layers, which form the glass effect, and a ContentPresenter to hold the content of the button. All the layers are laid out in a grid, on top of each other. There are also triggers defined, for mouse over and pressed states of the button, to add some interactivity.

I've defined a keyed window resource with the style, but the key can be removed to apply the style to all the buttons in the window.

OK, let's go through each of the layers to see how this cool glass look, widely used in Microsoft products, is achieved.

Button Layers

Background Layer

The first layer is an ellipse which is actually the canvas for drawing the reflection and the refraction layers. Nothing fancy, the fill color of the ellipse is wired to the Background property of the button.

Here is the screenshot of the Blend workspace with a dark blue ellipse:

layer1.png

<!-- Background Layer -->
<Ellipse Fill="{TemplateBinding Background}"/>

Refraction Layer

The second layer imitates a refraction of light falling from top to bottom of the button. The reason this layer is being drawn before the reflection layer is that the reflection layer must have a hard edge somewhere in the middle of the button ellipse, to get the shiny glass look. This layer is actually another ellipse, but this time, we will use a radial white-to-transparent gradient fill, to imitate the light refraction. The gradient starts from the bottom middle, and goes to the top middle of the ellipse; however, to decrease the intensity of the refraction highlight, the gradient starts way below the bottom edge of the ellipse. This can be clearly seen on the screenshot and the XAML code below:

layer2.png

<!-- Refraction Layer -->
<Ellipse x:Name="RefractionLayer">
  <Ellipse.Fill>
  <RadialGradientBrush GradientOrigin="0.496,1.052">
    <RadialGradientBrush.RelativeTransform>
      <TransformGroup>
        <ScaleTransform CenterX="0.5" 
          CenterY="0.5" ScaleX="1.5" ScaleY="1.5"/>
        <TranslateTransform X="0.02" Y="0.3"/>
      </TransformGroup>
    </RadialGradientBrush.RelativeTransform>
    <GradientStop Offset="1" Color="#00000000"/>
    <GradientStop Offset="0.4" Color="#FFFFFFFF"/>
    </RadialGradientBrush>
  </Ellipse.Fill>
</Ellipse>

Reflection Layer

The third layer is the light reflection layer, which is, actually, the hardest part of the effect. The problem is that the reflection cannot be drawn using any of the standard shapes. So, I decided to use a Path to draw the region of the reflection. Of course, it is possible to draw the path manually, but, frankly, this is not something somebody may enjoy (unless you are an artist and have a nice digitizer). Anyway, I drew yet another ellipse in MS Blend and converted it to a Path, then I just played around with Path Bezier points to get the final smooth path. The good thing about the Path is that you can apply the gradients to a complex Path object, exactly like you would do for any other predefined shape primitives like Ellipse or Rectangle. So, to get the shiny reflection, we will need a transparent-to-white radial gradient fill, starting from the bottom of the path (somewhere in the middle of the button) up to the top edge. I guess if I was an artist, I would be able to formalize the gradient stop for the radial gradient. But, I'm not an artist, so I kept playing with the settings in Blend, until I got the look I was happy with. And, since we have our button laid out in a grid, we should set VerticalAlignment="Top", so the reflection region doesn't end up in the center of the button.

layer3.png

<!-- Reflection Layer -->
<Path x:Name="ReflectionLayer" VerticalAlignment="Top" Stretch="Fill">
  <Path.RenderTransform>
    <ScaleTransform ScaleY="0.5" />
  </Path.RenderTransform>
  <Path.Data>
    <PathGeometry>
      <PathFigure IsClosed="True" StartPoint="98.999,45.499">
        <BezierSegment Point1="98.999,54.170" Point2="89.046,52.258" 
           Point3="85.502,51.029"/>
        <BezierSegment IsSmoothJoin="True" Point1="75.860,47.685" 
           Point2="69.111,45.196" Point3="50.167,45.196"/>
        <BezierSegment Point1="30.805,45.196" Point2="20.173,47.741" 
           Point3="10.665,51.363"/>
        <BezierSegment IsSmoothJoin="True" Point1="7.469,52.580" 
           Point2="1.000,53.252" Point3="1.000,44.999"/>
        <BezierSegment Point1="1.000,39.510" Point2="0.884,39.227" 
           Point3="2.519,34.286"/>
        <BezierSegment IsSmoothJoin="True" Point1="9.106,14.370" 
           Point2="27.875,0" Point3="50,0"/>
        <BezierSegment Point1="72.198,0" Point2="91.018,14.466" 
           Point3="97.546,34.485"/>
        <BezierSegment IsSmoothJoin="True" Point1="99.139,39.369" 
           Point2="98.999,40.084" Point3="98.999,45.499"/>
      </PathFigure>
    </PathGeometry>
  </Path.Data>
  <Path.Fill>
    <RadialGradientBrush GradientOrigin="0.498,0.526">
      <RadialGradientBrush.RelativeTransform>
        <TransformGroup>
          <ScaleTransform CenterX="0.5" 
            CenterY="0.5" ScaleX="1" ScaleY="1.997"/>
          <TranslateTransform X="0" Y="0.5"/>
        </TransformGroup>
      </RadialGradientBrush.RelativeTransform>
      <GradientStop Offset="1" Color="#FFFFFFFF"/>
      <GradientStop Offset="0.85" Color="#92FFFFFF"/>
      <GradientStop Offset="0" Color="#00000000"/>
    </RadialGradientBrush>
  </Path.Fill>
</Path>

Finally, I added a ContentPresenter to the center of the button. Some experiments show that moving the content area down for 1 pixel gives a more nice looking button, so a margin is also applied to the ContentPresenter (remember that, since the content area is centered in the grid, the top margin of 2px is actually moving it down by a pixel).

<!-- ContentPresenter -->
<ContentPresenter Margin="0,2,0,0" 
  HorizontalAlignment="Center" VerticalAlignment="Center"/>

OK, here is the final look of the glass button in the Blend workspace:

final.png

Adding Some Interactivity

Now, let's add some interactivity to our button.

Mouse Hover Effect

To get a mouse over effect, we will need to increase the intensity of the light source. To do so, let's define a trigger for the IsMouseOver event, copy-paste the gradient setting for both the reflection and refraction layers, and fine tune the gradient alpha values and offsets to highlight the button. For the refraction layer, I just moved the source of the radial gradient a little bit higher, whereas for the reflection layer, I changed the gradient stops to be less transparent white.

<Trigger Property="IsMouseOver" Value="True">
  <Setter TargetName="RefractionLayer" Property="Fill">
    <Setter.Value>
      <RadialGradientBrush GradientOrigin="0.496,1.052">
        <RadialGradientBrush.RelativeTransform>
          <TransformGroup>
            <ScaleTransform CenterX="0.5" CenterY="0.5" 
               ScaleX="1.5" ScaleY="1.5"/>
            <TranslateTransform X="0.02" Y="0.3"/>
          </TransformGroup>
        </RadialGradientBrush.RelativeTransform>
      <GradientStop Offset="1" Color="#00000000"/>
      <GradientStop Offset="0.45" Color="#FFFFFFFF"/>
      </RadialGradientBrush>
    </Setter.Value>
  </Setter>
  <Setter TargetName="ReflectionLayer" Property="Fill">
    <Setter.Value>
      <RadialGradientBrush GradientOrigin="0.498,0.526">
        <RadialGradientBrush.RelativeTransform>
          <TransformGroup>
            <ScaleTransform CenterX="0.5" CenterY="0.5" 
               ScaleX="1" ScaleY="1.997"/>
            <TranslateTransform X="0" Y="0.5"/>
          </TransformGroup>
        </RadialGradientBrush.RelativeTransform>
        <GradientStop Offset="1" Color="#FFFFFFFF"/>
        <GradientStop Offset="0.85" Color="#BBFFFFFF"/>
        <GradientStop Offset="0" Color="#00000000"/>
      </RadialGradientBrush>
    </Setter.Value>
  </Setter>
</Trigger>

Mouse Pressed Effect

For the IsPressed event, we will need to decrease the highlights. So, we need to move the reflection layer light source (radial gradient start point) a little bit down, and for the reflection layer, we need to make the gradient stops more transparent, to dim the light falling on our button.

<Trigger Property="IsPressed" Value="True">
  <Setter TargetName="RefractionLayer" Property="Fill">
    <Setter.Value>
      <RadialGradientBrush GradientOrigin="0.496,1.052">
        <RadialGradientBrush.RelativeTransform>
          <TransformGroup>
            <ScaleTransform CenterX="0.5" CenterY="0.5" 
               ScaleX="1.5" ScaleY="1.5"/>
            <TranslateTransform X="0.02" Y="0.3"/>
          </TransformGroup>
        </RadialGradientBrush.RelativeTransform>
        <GradientStop Offset="1" Color="#00000000"/>
        <GradientStop Offset="0.3" Color="#FFFFFFFF"/>
      </RadialGradientBrush>
    </Setter.Value>
  </Setter>
  <Setter TargetName="ReflectionLayer" Property="Fill">
    <Setter.Value>
      <RadialGradientBrush GradientOrigin="0.498,0.526">
        <RadialGradientBrush.RelativeTransform>
          <TransformGroup>
            <ScaleTransform CenterX="0.5" CenterY="0.5" 
               ScaleX="1" ScaleY="1.997"/>
            <TranslateTransform X="0" Y="0.5"/>
          </TransformGroup>
        </RadialGradientBrush.RelativeTransform>
        <GradientStop Offset="1" Color="#CCFFFFFF"/>
        <GradientStop Offset="0.85" Color="#66FFFFFF"/>
        <GradientStop Offset="0" Color="#00000000"/>
      </RadialGradientBrush>
    </Setter.Value>
  </Setter>
</Trigger>

Again, the values for the gradient stops have been chosen experimentally, I cannot give any exact values for getting the glassy look.

Using the Code

To use the style, just merge the style resource defined in the GlassButton.xaml file with the resource of your Window or Page and set the style of the button to {StaticResource GlassButton}. To set the color of the button, use the button's Background property:

<Window x:Class="GlassButton.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Glass Buttons" Height="228" Width="272">

  <Window.Resources>
    <ResourceDictionary>
      <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary Source="Resources\GlassButton.xaml"/>
      </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
  </Window.Resources>
  
  <Grid> 
    <Button Style="{StaticResource GlassButton}" Width="50" 
       Height="50" Background="#FF660707" Margin="10"/> 
  </Grid>
</Window>

Here is a demo (you can find the source code attached to the article) with some buttons with icons on a nice background (borrowed from the Windows Vista wallpapers):

main.jpg

History

  • 05 Jan., 2009: Initial version.

License

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

Share

About the Author

Lev Danielyan
Software Developer (Senior) Virage Logic Corporation
Armenia Armenia
I'm a developer focusing on Quality Assurance at the Virage Logic Corporation.
My main responsibilities include the development and maintenance of an environment for automated quality assurance of the Verilog IP provided by our company.

Comments and Discussions

 
GeneralMy vote of 5 PinmemberPaul Millar29-Oct-11 9:17 

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
Web04 | 2.8.141223.1 | Last Updated 5 Jan 2009
Article Copyright 2009 by Lev Danielyan
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid