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:
<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:
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.
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
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
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
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
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
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:
<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.
<controls:IconButton Height="30" Text="Home"
PathData="M10,20V14H14V20H19V12H22L12,3L2,12H5V20H10Z"/>
And the button would look like:
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.
History
- 26th April, 2020: Initial version