avatar

Creating custom glass buttons with XAML in WPF

by on Monday, May 4th, 2009, under Computer Programming, Tutorials

WPF XAML Glass Button

Glass Button

Today, we are going to take a look at how one would create a custom style for a WPF (Windows Presentation Foundation) button control. We will change the bland default button into a glass button! When working with the Windows Presentation Foundation to style a button, we simply create an XAML document to hold the style, and then we will apply the style within the form designer (much like how CSS styles are applied to HTML elements). So, lets get started! :)

Create a resource dictionary in your WPF Application project by right-clicking on your project in the Solution Explorer, and choosing Add -> New Item.  Select Resource dictionary and name it GlassButton.xaml

wpf-xaml-create-a-resource-dictionary

In the newly created resource dictionary, we will define a Style that we can apply to our buttons to change the way they look.  Initially, your GlassButton.xaml file will have only the following code:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
 
</ResourceDictionary>

We want to add a Style element so that we can change the properties of our control:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <Style x:Key="GlassButton" TargetType="{x:Type Button}">
    <Setter Property="FontSize" Value="42" />
    <Setter Property="Foreground" Value="White" />
    <Setter Property="Template">
      <Setter.Value>
        <ControlTemplate TargetType="{x:Type Button}">
 
        </ControlTemplate>
      </Setter.Value>
    </Setter>
  </Style>
</ResourceDictionary>

The styles “x:Key” attribute is what we will use to identify the style later when we apply it to a button control. The “TargetType” attribute declares what type of control our style will be used for, and in our case, it is the button. At this point, we have a couple of default properties setup for our control; the font is enlarged and the foreground color is changed to white. Next, we need to change the template property for the control to make it look like a glass button. You will notice that this property is structured a little bit differently. That is because we are defining the template inline and this requires the creation of a complex object, the ControlTemplate.

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <Style x:Key="GlassButton" TargetType="{x:Type Button}">
    <Setter Property="FontSize" Value="42" />
    <Setter Property="Foreground" Value="White" />
    <Setter Property="Template">
      <Setter.Value>
        <ControlTemplate TargetType="{x:Type Button}">
          <Border x:Name="ButtonBorder" 
                  CornerRadius="25" 
                  BorderThickness="4" 
                  Background="#AA000000"  
                  BorderBrush="#99FFFFFF"
                  RenderTransformOrigin="0.5,0.5">
              <ContentPresenter x:Name="ButtonContentPresenter"
                                VerticalAlignment="Center"  
                                HorizontalAlignment="Center"/>
          </Border>
        </ControlTemplate>
      </Setter.Value>
    </Setter>
  </Style>
</ResourceDictionary>

Now that we have the template declared, lets walk through what we did. First, we laid out the main border and the tinted background of the button by using a “Border” element. We gave the button border a name (used later) and we also gave our button rounded corners by setting the CornerRadius to 25 units on each corner. You could also set the radius unit of each individual corner by specifying “25,25,25,25″ which will define the top-left radius and continue clockwise around to the other corners.

Note that I said units and not pixels. One of the benefits of the Windows Presentation Foundation is it will display graphics based on the dpi setting of your device. For example, if your dpi settings are correct in your OS, a button that is 1″ wide on a 15″ screen running at 1024×768 would also be exactly 1″ wide on a 20″ screen running at 1024×768. If the controls were pixel based, the first button would be 1″ wide and the later button would be about 1 1/3″ wide.

Next we set the width of all borders to 4 units wide and apply some colors to the element. The colors used are in HTML color format, but there is a twist. XAML colors support an alpha channel (a.k.a. transparency) so the hexadecimal value we use represents the alpha, red, green and blue channels. Since we want our button background to look a little tinted, we will use the color black and make it partially transparent (#AA000000 – In decimal, this is Alpha=170, Red=0, Green=0, Blue=0). We also want the border to be white and partially transparent, so we use #99FFFFFF. The final property we set for the main border element is the RenderTransformOrigin. This is a percent based x,y value that indicates where the control will be pinned down when a transform later changes the control. We will discuss this more later, but for now, set the origin to the center of the control, 0.5, 0.5 (x origin is 50% across, y origin is 50% down).

WPF XAML Flat Glass Button

WPF XAML Flat Glass Button

The “ContentPresenter” element is the last item we add in this step. This element is what will display the text on the face of the button, so we obviously will want this inside the border. All we do here is give it a name for good practice and center the text.

Now you have a simple button style that looks like a flat piece of glass. If you want to use this style now so that you can see what it looks like, skip ahead to the “Applying our style to a control” section.

Now its time to add a little depth to our button and make a reflection on it so it looks curved and shiny. To do this we will update the code as follows:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <Style x:Key="GlassButton" TargetType="{x:Type Button}">
    <Setter Property="FontSize" Value="42" />
    <Setter Property="Foreground" Value="White" />
    <Setter Property="Template">
      <Setter.Value>
        <ControlTemplate TargetType="{x:Type Button}">
          <Border x:Name="ButtonBorder" 
                  CornerRadius="25" 
                  BorderThickness="4" 
                  Background="#AA000000"  
                  BorderBrush="#99FFFFFF"
                  RenderTransformOrigin="0.5,0.5">
            <Grid>
              <Grid.RowDefinitions>
                <RowDefinition Height="*"/>
                <RowDefinition Height="1.7*"/>
              </Grid.RowDefinitions>
              <Border Grid.Row="0" CornerRadius="23,23,0,0">
                <Border.Background>
                  <LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1">
                    <GradientStop Color="#08FFFFFF" Offset="0"/>
                    <GradientStop Color="#88FFFFFF" Offset="1"/>
                  </LinearGradientBrush>
                </Border.Background>
              </Border>
              <ContentPresenter x:Name="ButtonContentPresenter"
                                VerticalAlignment="Center"  
                                HorizontalAlignment="Center" 
                                Grid.RowSpan="2" />
            </Grid>
          </Border>
        </ControlTemplate>
      </Setter.Value>
    </Setter>
  </Style>
</ResourceDictionary>

We have added a Grid element to our style because a Border element can only have one child, but we want a reflection and a content presenter. The grid can have multiple children and it will allow for proper placement of our reflection. The Grid has 2 rows defined, and the only attribute we set for each row is the height. Note the ” * ” in use. What this will do is make sure all rows fill up 100% of the grid. The number in front of the asterisk is a multiplier, so in our case, row 2 is 1.7 times the height of row one. If I had used 2*, the second row would be twice as high as the other row (i.e. 2/3 the height of the grid).

Now we add a border object to simulate the reflection. You can see that we are binding the border to the first row in the grid by setting the Grid.Row attribute to 0, and we are also making the top-left and top-right corners rounded to fit into the outer border. With this border element, we are setting the background property to resemble the reflection, but we don’t use a simple color like we have done in the past. We instead define a LinearGradientBrush to blend from a mostly transparent white to a partially transparent white. The StartPoint and EndPoint attributes of the LinearGradientBrush are percent based x,y coordinates that will determine where the blending starts, ends, and in what direction the blend will occur. Using 0.5,0 will start at the top center, and 0.5,1 will stop at the bottom center of the border object. There are also 2 GradientStops defined here that represent the transparent white and the semi-transparent white colors that the gradient will use. The offset attribute will determine (as a percent based decimal) where the color should occur. For our case, we want the transparent white to occur at the beginning of the gradient, and the semi-transparent white to occur at the end. Imagine if your gradient had 3 colors, you might put them at offsets of 0, 0.5, and 1 to see an equal blend distance between each color.

Finally, we move the content presenter element into the grid object and add the attribute Grid.RowSpan=”2″ so that it can span the entire height of the grid.

Now we have a pretty glass button, but it wouldn’t do much if you were to click on it. We will create a trigger to shrink the button a little bit so it looks like it is depressing when you click on it. Update your code as follows:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <Style x:Key="GlassButton" TargetType="{x:Type Button}">
    <Setter Property="FontSize" Value="42" />
    <Setter Property="Foreground" Value="White" />
    <Setter Property="Template">
      <Setter.Value>
        <ControlTemplate TargetType="{x:Type Button}">
          <Border x:Name="ButtonBorder" 
                  CornerRadius="25,25,25,25" 
                  BorderThickness="4,4,4,4" 
                  Background="#AA000000"  
                  BorderBrush="#99FFFFFF"
                  RenderTransformOrigin="0.5,0.5">
            <Grid>
              <Grid.RowDefinitions>
                <RowDefinition Height="*"/>
                <RowDefinition Height="1.7*"/>
              </Grid.RowDefinitions>
              <Border Grid.Row="0" CornerRadius="23,23,0,0">
                <Border.Background>
                  <LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1">
                    <GradientStop Color="#08FFFFFF" Offset="0"/>
                    <GradientStop Color="#88FFFFFF" Offset="1"/>
                  </LinearGradientBrush>
                </Border.Background>
              </Border>
              <ContentPresenter x:Name="ButtonContentPresenter"
                                VerticalAlignment="Center"  
                                Grid.RowSpan="2" 
                                HorizontalAlignment="Center"/>
            </Grid>
          </Border>
          <ControlTemplate.Triggers>
            <Trigger Property="IsPressed" Value="True">
              <Setter Property="RenderTransform" TargetName="ButtonBorder">
                <Setter.Value>
                  <TransformGroup>
                    <ScaleTransform ScaleX="0.9" ScaleY="0.9"/>
                  </TransformGroup>
                </Setter.Value>
              </Setter>
            </Trigger>
          </ControlTemplate.Triggers>
        </ControlTemplate>
      </Setter.Value>
    </Setter>
  </Style>
</ResourceDictionary>

In this update, we have created a trigger event that will occur when the buttons “IsPressed” property is true. When this occurs, we will apply a RenderTransform to the element named “ButtonBorder”. In our particular case, we are using a ScaleTransform to scale down the size of our button border to 90% when pressed. If you remember correctly, we set a RenderTransformOrigin to the center of the button in a previous code snippet. This will center the transform action so the button will appear to adjust evenly around the edges with the center locked in place. If we did not set this, the button would do all scaling while pinned in the top left corner, and only the right side and the bottom would visibly move. If that doesn’t make sense, just try it and you’ll see what I mean. ;)

Applying our style to a control

To use our GlassButton style, we must now add a resource reference into our App.xaml file. My App.xaml looks like the following; I have only added the <ResourceDictionary Source=”GlassButton.xaml”/> line:

<Application x:Class="Demo.App"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  StartupUri="Window1.xaml">
  <Application.Resources>
    <ResourceDictionary Source="GlassButton.xaml"/>
  </Application.Resources>
</Application>

Finally, open the XAML code for your window and add a button element with the style attribute set to “{DynamicResource GlassButton}” (note that GlassButton is the x:Key value we set for the style element). In my particular application, the line of code looks like this:

    <Button Style="{DynamicResource GlassButton}" 
            Grid.Column="1" 
            Margin="0,0,18.577,17.148" 
            Name="button1" 
            Height="91.456" 
            VerticalAlignment="Bottom" 
            HorizontalAlignment="Right" 
            Width="207.764">Button</Button>

Now when you run your application, you will have a beautiful glass button!

Tags: , , , , ,

Monday, May 4th, 2009 Computer Programming, Tutorials

7 Comments to Creating custom glass buttons with XAML in WPF

  • avatar Aditya says:

    Hi Guys,
    Great Job. I really liked the explanation after every code snippet. I have learnt few things in xaml today. I liked the way rendertransformorigin explanation was made. I appreciate it

    I would to learn more abt xaml and wpf.. can u pls refer few sources, or else learn the examples this way and start implementing it??

    Thanks a lot
    Aditya

  • avatar Hansani says:

    the code wrked and it is great. can tell me how to apply a specific theme into an application using the same way as shown above.
    Thank You.

  • avatar Tony says:

    Nice one Jade – simple, clear and extremely useful.

  • avatar Syamala says:

    Hi
    I tried this custom glass button.It is very awesome

  • How does TextBlock.Foreground property setter in the style of a Button get applied..

  • avatar mike says:

    Hi Jade

    Really nice tutorial! Btw i have a query:

    “note that GlassButton is the x:Key value we set for the style element). In my particular application, the line of code looks like this:”

    I do not quite get this line. Plus i do get an error (Invalid attribute value {x:Type Button} for property TargetType.) when i run the code.

    Is there something im missing out? Thanks! :>

    • avatar Jade says:

      If you look at the second block of code and the text afterwards, you will see where we set the x:Key=”GlassButton”. I was referencing that the “GlassButton” text used in the line below was referencing that earlier defined style:

  • Leave a Reply