WPF button as custom control with text and vector icon

1.1k Views Asked by At

Good evening guys,

I'd really like to have a custom button with text and vector icon.. I searched for something similar but no luck..so I tried by myself to make a custom control but I am not able to make it works: no errors or warnings found but the icon doesn't appear at all. Here below my code:

Custom control CS:

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

    public static readonly DependencyProperty IconProperty =
        DependencyProperty.Register("Icon", typeof(Path), typeof(ThButton), new UIPropertyMetadata(null));

    public Path Icon
    {
        get { return (Path)GetValue(IconProperty); }
        set { SetValue(IconProperty, value); }
    }

    public static readonly DependencyProperty ThLabelProperty =
        DependencyProperty.Register("ThLabel", typeof(string), typeof(ThButton), new UIPropertyMetadata(null));

    public String ThLabel
    {
        get { return (String)GetValue(ThLabelProperty); }
        set { SetValue(ThLabelProperty, value); }
    }

}

generic.xaml (triggers etc. removed since not useful for this):

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:CustomControlTest">

    <Path x:Key="BoatIcon" Fill="Black" Data="M6,6H18V9.96L12,8L6,9.96M3.94,19H4C5.6,19 7,18.12 8,17C9,18.12 10.4,19 12,19C13.6,19 15,18.12 16,17C17,18.12 18.4,19 20,19H20.05L21.95,12.31C22.03,12.06 22,11.78 21.89,11.54C21.76,11.3 21.55,11.12 21.29,11.04L20,10.62V6C20,4.89 19.1,4 18,4H15V1H9V4H6A2,2 0 0,0 4,6V10.62L2.71,11.04C2.45,11.12 2.24,11.3 2.11,11.54C2,11.78 1.97,12.06 2.05,12.31M20,21C18.61,21 17.22,20.53 16,19.67C13.56,21.38 10.44,21.38 8,19.67C6.78,20.53 5.39,21 4,21H2V23H4C5.37,23 6.74,22.65 8,22C10.5,23.3 13.5,23.3 16,22C17.26,22.65 18.62,23 20,23H22V21H20Z" />

    <Color x:Key="BackgroundColor1" A="255" R="0" G="133" B="209"/>
    <Color x:Key="BackgroundColor2" A="255" R="0" G="61" B="94"/>

    <Color x:Key="MouseOverBackgroundColor1" A="255" R="0" G="156" B="231"/>
    <Color x:Key="MouseOverBackgroundColor2" A="255" R="0" G="90" B="155"/>

    <Color x:Key="MousePressedBackgroundColor1" A="255" R="0" G="98" B="195"/>
    <Color x:Key="MousePressedBackgroundColor2" A="255" R="0" G="36" B="72"/>

    <Color x:Key="IsNotEnabledBackgroundColor1" A="255" R="233" G="233" B="233"/>
    <Color x:Key="IsNotEnabledBackgroundColor2" A="255" R="240" G="240" B="240"/>

    <SolidColorBrush x:Key="ThBorderBrush" Color="#ECECEC"></SolidColorBrush>

    <Style TargetType="{x:Type local:ThButton}">
        <Setter Property="BorderBrush" Value="{StaticResource ThBorderBrush}"/>
        <Setter Property="BorderThickness" Value="2"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:ThButton}">
                    <Border x:Name="t"
                            Margin="{TemplateBinding Margin}"
                            BorderBrush="{TemplateBinding BorderBrush}"
                            BorderThickness="{TemplateBinding BorderThickness}"
                            CornerRadius="6">

                        <Border.Background>
                            <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
                                <LinearGradientBrush.GradientStops>
                                    <GradientStop x:Name="BackgroundGradientStop1" Offset="0" Color="{StaticResource BackgroundColor1}"/>
                                    <GradientStop x:Name="BackgroundGradientStop2" Offset="1" Color="{StaticResource BackgroundColor2}"/>
                                </LinearGradientBrush.GradientStops>
                            </LinearGradientBrush>
                        </Border.Background>

                        <StackPanel Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center">
                            <Rectangle Width="20" Height="20" Fill="black" Margin="0,0,0,5">
                                <Rectangle.OpacityMask>
                                    <VisualBrush Stretch="Fill" Visual="{TemplateBinding Icon}"/>
                                </Rectangle.OpacityMask>
                            </Rectangle>
                            <Label Content="{TemplateBinding ThLabel}"/>
                        </StackPanel>

                    </Border>

                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

</ResourceDictionary>

And finally mainWindow.xaml:

<local:ThButton Width="100" Icon="{StaticResource BoatIcon}" ThLabel="Test"/>

if I put the "{StaticResource BoatIcon}" in place of "{TemplateBinding Icon}" in Visual (generic.xaml) the icon appears so I suppose the problem is all about how I tried to use the DependencyProperty.

1

There are 1 best solutions below

0
On

This can be done using a custom UserControl quite easily.

First, add your image to your project resources, and make 'Build Type = Resource' in the properties. Do this by selecting the newly added image and right clicking, then setting the above property.

Next, create a UserControl. Here's my XAML

<UserControl x:Class="ListViewDragAndDrop.UCPictureButton"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:ListViewDragAndDrop"
             mc:Ignorable="d" 
             d:DesignHeight="32" d:DesignWidth="128">
    <Grid>
        <Button Click="Button_Click">
            <StackPanel Orientation="Horizontal">
                <Image Source="/Resources/solution.png"
                       Margin="4, 4, 8, 4"/>
                <TextBlock Text="Button Text"
                           HorizontalAlignment="Center" VerticalAlignment="Center"/>
            </StackPanel>
        </Button>  
    </Grid>
</UserControl>

Make sure the 'Source' of the image is correct. What's shown above is where it's in my project which is the default.

Then the UserControl code behind:

public partial class UCPictureButton : UserControl
{
    public event RoutedEventHandler RoutedButtonClick;

    public UCPictureButton()
    {
        InitializeComponent();
    }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        RoutedButtonClick?.Invoke(this, e);
    }
}

In the button click event, we invoke the RoutedEvent we declared there.

Now on your Main window or wherever you want to use the button:

<local:UCPictureButton Grid.Column="2" Width="120" Height="32">
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="RoutedButtonClick">
            <i:InvokeCommandAction Command="{Binding CmdButtonClick}"/>
        </i:EventTrigger>
    </i:Interaction.Triggers>
</local:UCPictureButton>

The i namespace is the interactivity namespace:

xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"

Now all you have to do is in your ViewModel simply hook up a method to the above declared CmdButtonClick command.

I'm using the MVVM Light toolkit and it looks like this:

public RelayCommand CmdButtonClick { get; private set; }

public MainViewModel(IDataService dataService)
{
    CmdButtonClick = new RelayCommand(ButtonClick);
}

private void ButtonClick()
{
    // Button action
}

Result:

enter image description here