Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

A Simple Cross Button for WPF

0.00/5 (No votes)
18 Aug 2011 2  
A small and simple cross button for your WPF applications.

Screenshot.png

Introduction

In this article, I'll show you how to create a very simple control to represent a small cross button - the sort that you see in the tabbed windows or certain controls.

Background

I found that when creating a custom tab control that allowed the user to close the tabs with a small button, a control like this would be useful. However, in later projects, this small control has been surprisingly versatile.

Creating a Custom Control

Let's get started. Create a new Visual Studio Solution and add a WPF Custom Control Library to it. The custom control code is very simple:

/// <summary>
/// The Cross Button is a very simple version
/// of the button that displays as a discrete cross,
/// similar to the buttons at the top of Google Chrome's tabs.
/// </summary>
public class CrossButton : Button
{
    /// <summary>
    /// Initializes the <see cref="CrossButton"/> class.
    /// </summary>
    static CrossButton()
    {
        //  Set the style key, so that our control template is used.
        DefaultStyleKeyProperty.OverrideMetadata(typeof(CrossButton), 
               new FrameworkPropertyMetadata(typeof(CrossButton)));
    }
}

Now we are deriving the control from Button; the only thing this code really does is makes sure that the style we use for the CrossButton is the correct style that we will add in our Generic.xaml dictionary.

We actually have no custom code in the control - we could define the whole thing as a style that just re-templates the button. However, in my local version, I have added a few more features - if you have a class like this, you can extend yours too.

The XAML

The XAML in the Generic.xaml file is basic as well:

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:CrossButton">
    <Style TargetType="{x:Type local:CrossButton}">

Generic.xaml is a resource dictionary, we need a reference to the namespace we have defined in the control in. The only thing we have in the resource dictionary is the style for the cross button.

Note: If you want to implement this control simply as a style, just set the target type to 'Button' and give the style a key - then apply the style to any button.

Moving on, we'll need a few resources for the button.

<!-- Brushes we use for the control. -->
<Style.Resources>
    <SolidColorBrush x:Key="NormalBackgroundBrush" Color="#00000000" />
    <SolidColorBrush x:Key="NormalBorderBrush" Color="#FFFFFFFF" />
    <SolidColorBrush x:Key="NormalForegroundBrush" Color="#FF8f949b" />

    <SolidColorBrush x:Key="HoverBackgroundBrush" Color="#FFc13535" />
    <SolidColorBrush x:Key="HoverForegroundBrush" Color="#FFf9ebeb" />

    <SolidColorBrush x:Key="PressedBackgroundBrush" Color="#FF431e20" />
    <SolidColorBrush x:Key="PressedBorderBrush" Color="#FF110033" />
    <SolidColorBrush x:Key="PressedForegroundBrush" Color="#FFf9ebeb" />
</Style.Resources> 

These are the brushes we'll use to paint the button in its various states.

<!-- Simple properties that we set. -->
<Setter Property="Cursor" Value="Hand" />
<Setter Property="Focusable" Value="False" />

This is such a small button that it'll help to have the cursor as a hand so that it is obvious it is clickable. Also, by setting the Focusable style to False, we stop the user from tabbing to the control which in most circumstances would be a bit odd.

Here's where it gets more interesting:

<!-- The control template. -->
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}"> 

This is where we are going to change the control's template. The control template is what is used to draw the control - by changing the template, we can completely change how we draw the control.

<Grid Background="Transparent">

<!-- The background of the button, as an ellipse. -->
<Ellipse x:Name="backgroundEllipse" />

<!-- A path that renders a cross. -->
<Path x:Name="ButtonPath"
      Margin="3"
      Stroke="{StaticResource NormalForegroundBrush}"
      StrokeThickness="1.5"
      StrokeStartLineCap="Square"
      StrokeEndLineCap="Square"
      Stretch="Uniform"
      VerticalAlignment="Center"
      HorizontalAlignment="Center">

The cross button is a simple looking button - a small cross with a red circle behind it when the mouse is over it. We put the ellipse and path (which will draw the cross) in a grid, drawing one on top of the other.

We have the path, but we'll need to define the geometry for it:

        <Path.Data>
            <PathGeometry>
                <PathGeometry.Figures>
                    <PathFigure StartPoint="0,0">
                        <LineSegment Point="25,25"/>
                    </PathFigure>
                    <PathFigure StartPoint="0,25">
                        <LineSegment Point="25,0"/>
                    </PathFigure>
                </PathGeometry.Figures>
            </PathGeometry>
        </Path.Data>
    </Path>
</Grid>

The geometry is very simple - two line segments! We can close the path and the grid and now move onto the more interactive features. We'll use triggers to change colours as the mouse moves over the control.

<!-- The triggers. -->
<ControlTemplate.Triggers>
    <Trigger Property="IsMouseOver" Value="True">
        <Setter TargetName="backgroundEllipse" Property="Fill"
                Value="{StaticResource HoverBackgroundBrush}" />
        <Setter TargetName="ButtonPath" Property="Stroke" 
                Value="{StaticResource HoverForegroundBrush}"/>
    </Trigger> 

The first trigger is fired when the mouse moves over - simply changing the colour of the background ellipse and the button path.

<Trigger Property="IsEnabled" Value="false">
    <Setter Property="Visibility" Value="Collapsed"/>
</Trigger>

If the control isn't enabled, we'll just hide it - it's a very discrete little button.

                        <Trigger Property="IsPressed" Value="true">
                            <Setter TargetName="backgroundEllipse" Property="Fill"
                                    Value="{StaticResource PressedBackgroundBrush}" />
                            <Setter TargetName="backgroundEllipse" Property="Stroke"
                                    Value="{StaticResource PressedBorderBrush}" />
                            <Setter TargetName="ButtonPath" Property="Stroke" 
                                    Value="{StaticResource PressedForegroundBrush}"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

We use the final trigger to colour the control black and white when it is pressed. After this, we close all of the tags and we're done with the dictionary.

Example

The example application shows how to use the control in a data template to remove items from a list:

Screenshot.png

This is just one use for the control - below is another (not in the example):

Screenshot2.png

If anyone would find the above control useful, let me know and I'll write an article about it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here