Click here to Skip to main content
15,860,972 members
Articles / Desktop Programming / WPF

A Style for Round Glassy WPF Buttons

Rate me:
Please Sign up or sign in to vote.
4.99/5 (113 votes)
5 Jan 2009CPOL5 min read 381.7K   19.9K   161   54
A style for giving WPF buttons a glassy, Windows Vista-like look

glassbuttons/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:

glassbuttons/layer1.png

XML
<!-- 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:

glassbuttons/layer2.png

XML
<!-- 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.

glassbuttons/layer3.png

XML
<!-- 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).

XML
<!-- 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:

glassbuttons/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.

XML
<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.

XML
<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:

XML
<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):

glassbuttons/main.jpg

History

  • 05 January, 2009: Initial version

License

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


Written By
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

 
AnswerRe: how to use the dll in my application Pin
Lev Danielyan31-Dec-09 3:22
Lev Danielyan31-Dec-09 3:22 
GeneralRe: how to use the dll in my application Pin
kenobe31-Dec-09 20:46
kenobe31-Dec-09 20:46 
GeneralGreat Article Pin
wangling14-Sep-09 13:33
wangling14-Sep-09 13:33 
GeneralVery effective. Pin
Dennis_Aries3-Sep-09 22:49
Dennis_Aries3-Sep-09 22:49 
GeneralGreat article Pin
Burnhard15-Aug-09 8:55
Burnhard15-Aug-09 8:55 
QuestionHow about a silverlight compatible version? Pin
kainhart20-Feb-09 6:48
kainhart20-Feb-09 6:48 
AnswerRe: How about a silverlight compatible version? Pin
Lev Danielyan20-Feb-09 6:55
Lev Danielyan20-Feb-09 6:55 
GeneralRe: How about a silverlight compatible version? Pin
kainhart26-Feb-09 4:24
kainhart26-Feb-09 4:24 
Great, I look forward to it. In the mean time I've been looking at some other examples but I like the appearance of your buttons better than the others so far.
GeneralRe: How about a silverlight compatible version? Pin
dagzon9-Feb-10 1:58
dagzon9-Feb-10 1:58 
GeneralRe: How about a silverlight compatible version? Pin
Member 191342815-Feb-10 4:12
Member 191342815-Feb-10 4:12 
GeneralNice article Pin
LFirth12-Jan-09 4:29
LFirth12-Jan-09 4:29 
GeneralRe: Nice article Pin
Lev Danielyan12-Jan-09 5:39
Lev Danielyan12-Jan-09 5:39 
GeneralGreat article 5/5 Pin
Colin Eberhardt6-Jan-09 1:09
Colin Eberhardt6-Jan-09 1:09 
GeneralRe: Great article 5/5 Pin
Lev Danielyan6-Jan-09 1:15
Lev Danielyan6-Jan-09 1:15 
GeneralFinally Pin
VCSKicks5-Jan-09 20:41
VCSKicks5-Jan-09 20:41 
GeneralRe: Finally Pin
Lev Danielyan5-Jan-09 20:42
Lev Danielyan5-Jan-09 20:42 
GeneralKISS Pin
tamash_ionut5-Jan-09 7:18
tamash_ionut5-Jan-09 7:18 
GeneralRe: KISS Pin
Lev Danielyan5-Jan-09 8:17
Lev Danielyan5-Jan-09 8:17 
GeneralWow 5/5 Pin
prasad025-Jan-09 4:47
prasad025-Jan-09 4:47 
GeneralRe: Wow 5/5 Pin
Lev Danielyan5-Jan-09 4:57
Lev Danielyan5-Jan-09 4:57 

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

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