Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / WPF

Creating Custom WPF Icon Button

5.00/5 (12 votes)
26 Apr 2020CPOL3 min read 33.3K  
A walk-through for creating a custom WPF button that contains both icon and text
In this tip, you will learn the steps to create and use a custom WPF button that will contain both icon and text.

Introduction

Sometimes we need to use a button that contains both icon and text in WPF and to accomplish this, we have to add the text and image controls inside the button and repeat that every time we are going to use this button.

In the following sections, first we are going to add text and icon to the default WPF button, then create a custom button that supports icon and text.

Adding Icon and Text to Button

In order to create a button that accepts both vector icon and text, you can just add a button control and change its content to contain a StackPanel with Path and Label as its children like the sample below:

XML
<Button>
    <Button.Content>
        <StackPanel VerticalAlignment="Center" 
                    HorizontalAlignment="Center" Orientation="Horizontal" 
                    Background="Transparent" Height="25">
            <Viewbox Margin="0 3" >
                <Path Fill="Gray" 
                      VerticalAlignment="Center" HorizontalAlignment="Center" 
                      Data="M16,10H14V7H10V10H8L12,14M19,15H15A3,3 0 0,1 12,
                      18A3,3 0 0,1 9,15H5V5H19M19,3H5C3.89,3 3,3.9 3,5V19A2,2 0 0,
                      0 5,21H19A2,2 0 0,0 21,19V5A2,2 0 0,0 19,3Z"/>
            </Viewbox>
            <Label Content="Inbox" VerticalAlignment="Center" 
                   HorizontalAlignment="Center" VerticalContentAlignment="Center" 
                   HorizontalContentAlignment="Center" Margin="2 1" Padding="5" 
                   Foreground="Gray" FontSize="10" FontWeight="Bold"/>
        </StackPanel>
    </Button.Content>
</Button>

This button would look like this:

Image 1

In order to be able to reuse the previous button, we can define a style for this button and move all the code from the content tag to a style file and place this style to any other button. But if we want to create another button with different icon and/or text, here comes the need for a custom button.

Creating Custom Icon Button

Step 1: Create Custom Button Class

Create a class for our custom control named IconButton.cs that drives from the WPF Button class. Add the following static constructor inside.

C#
public class IconButton : Button
{
  static IconButton()
  {
     DefaultStyleKeyProperty.OverrideMetadata(typeof(IconButton), 
        new FrameworkPropertyMetadata(typeof(IconButton)));
  }
}

In the constructor, we override the default button style key as we are going to define a new default style for our custom button. If we removed this override, the custom button would use the default style for the WPF button.

Step 2: Add Dependency Properties

We need to be able to control the button's icon, text, icon and text orientation and the visibility of both icon and text. In order to do that, we would add five dependency properties for our button.

  • PathData to set the icon
  • Text to set the button's text
  • Orientation to change the icon and text orientation
  • TextVisibility
  • IconVisibility

PathData

C#
public static readonly DependencyProperty PathDataProperty =
DependencyProperty.Register(nameof(PathData), typeof(Geometry), 
                   typeof(IconButton), new PropertyMetadata(Geometry.Empty));

public Geometry PathData
{
get { return (Geometry)GetValue(PathDataProperty); }
set { SetValue(PathDataProperty, value); }
}

Text

C#
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register(nameof(Text), typeof(string), 
                            typeof(IconButton), new PropertyMetadata(string.Empty));

public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}

Orientation

C#
public static readonly DependencyProperty OrientationProperty =
DependencyProperty.Register(nameof(Orientation), 
    typeof(Orientation), typeof(IconButton), new PropertyMetadata(default(Orientation)));

public Orientation Orientation
{
     get { return (Orientation)GetValue(OrientationProperty); }
     set { SetValue(OrientationProperty, value); }
}

TextVisibility

C#
public static readonly DependencyProperty TextVisibilityProperty =
DependencyProperty.Register(nameof(TextVisibility), 
    typeof(Visibility), typeof(IconButton), new PropertyMetadata(default(Visibility)));

public Visibility TextVisibility
{
get { return (Visibility)GetValue(TextVisibilityProperty); }
set { SetValue(TextVisibilityProperty, value); }
}

IconVisibility

C#
public static readonly DependencyProperty IconVisibilityProperty =
DependencyProperty.Register(nameof(IconVisibility), 
    typeof(Visibility), typeof(IconButton), new PropertyMetadata(default(Visibility)));

public Visibility IconVisibility
{
get { return (Visibility)GetValue(IconVisibilityProperty); }
set { SetValue(IconVisibilityProperty, value); }
} 

Step 3: Add Default Style

Create a resource dictionary in a folder named Themes to add the default style for the IconButton. Name it Generic.xaml and add the following style to it:

XML
<Style TargetType="{x:Type local:IconButton}">
        <Setter Property="Orientation" Value="Horizontal"/>
        <Setter Property="Height" Value="24" />
        <Setter Property="Background" Value="Transparent" />
        <Setter Property="BorderThickness" Value="0" />
        <Setter Property="Cursor" Value="Hand" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:IconButton}">
                    <Border BorderThickness="{TemplateBinding BorderThickness}"
                            Background="{TemplateBinding Background}"
                            BorderBrush="{TemplateBinding BorderBrush}">
                        <Viewbox Stretch="Uniform">
                            <StackPanel Orientation="{TemplateBinding Orientation}" 
                                        Height="{TemplateBinding Height}"
                                    HorizontalAlignment="Center"
                                    VerticalAlignment="Center"
                                    Background="{TemplateBinding Background}">

                                <Viewbox Stretch="Uniform" HorizontalAlignment="Left" 
                                         VerticalAlignment="Center"
                                         Visibility="{TemplateBinding IconVisibility}" 
                                         Margin="2 5">

                                        <Path Stretch="Uniform"
                                              Fill="{TemplateBinding Foreground}" 
                                              Data="{TemplateBinding PathData}"/>

                                </Viewbox>

                                <ContentControl Content="{TemplateBinding Text}" 
                                                Visibility="{TemplateBinding TextVisibility}"
                                                HorizontalAlignment="Center" 
                                                VerticalAlignment="Center" 
                                                Margin="2"/>
                            </StackPanel>
                        </Viewbox>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
        <Style.Triggers>
            <Trigger Property="IsMouseOver" Value="True">
                <Setter Property="BorderBrush" Value="Transparent"/>
                <Setter Property="Opacity" Value="0.5"/>
            </Trigger>
            <Trigger Property="IsMouseOver" Value="False">
                <Setter Property="BorderBrush" Value="Transparent"/>
            </Trigger>
        </Style.Triggers>
    </Style>

This style defines a control template for the icon button. The template contains a view box that contains the icon path and a content control for adding the text wrapped inside a stack panel. The stack panel is placed inside another view box to resize both the text and icon to fill the buttons space.

We created this default style in the Themes folder as by default, WPF expects that any generic styles would be in a folder names Themes inside the project. If you prefer your default style to be stored elsewhere, you would have to create a resource dictionary inside the Themes folder and add your resource dictionary as a merged dictionary to it.

Using the Custom Icon button

Finally, we go to the main view and the following code to use our control.

XML
<controls:IconButton Height="30" Text="Home" 
PathData="M10,20V14H14V20H19V12H22L12,3L2,12H5V20H10Z"/>

And the button would look like:

Image 2

Full Source Code

You could find the full code for the custom button here. In this code, you would find also that we added a hover style for the custom button and the custom button is moved to a separate class library project.

Image 3

History

  • 26th April, 2020: Initial version

License

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