0

In a WPF application I have a style used for buttons:

<Style TargetType="Button" x:Key="ButtonEllipse">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="Button">
                <StackPanel Orientation="Vertical">
                    <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0 0 0 10"/>
                    <Image x:Name="ButtonImage" HorizontalAlignment="Center" VerticalAlignment="Center" Stretch="None" Source="/MyProject;component/Images/ButtonEllipse.png"/>
                </StackPanel>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

For most buttons that is OK but for one specific instance I want to use the same template but change the image to ButtonEllipseNew.png (which is the value of a view model property). The button is defined like this:

<Button Content="Test" Style="{StaticResource ButtonEllipse}">
</Button>

How can I change the value of the image source in the template only for this specific button? I want to bind the source to a property in the view model.

Vladimir
  • 1,425
  • 16
  • 31
  • {Binding ImageProp} on your viewmodel property ? – GCamel Mar 27 '18 at 15:09
  • The image is defined inside a style which is general (not always used in the same model). The question is exactly how to use binding only in one specific instance while leaving the rest with default value. – Vladimir Mar 27 '18 at 15:13
  • then just define a second style and change the image...or have to write a custom button control that define image dependency property – GCamel Mar 27 '18 at 15:22
  • It's dynamic (hence binding), I cannot write a style for each possible value – Vladimir Mar 27 '18 at 15:26
  • custom button control like other anwsers, by the way replace images by vectoriel content would be a must have - you can find on materialdesign.com or draw them with expresion designer 4 (free) - then no more style, no more custom button, just a content - https://github.com/TheCamel/ArchX/blob/master/ArchX/Views/RibbonView.xaml – GCamel Mar 27 '18 at 17:48

3 Answers3

4

I am afraid you can't reuse only a part of a ControlTemplate. You must define the template as a whole:

WPF: Is there a way to override part of a ControlTemplate without redefining the whole style?

What you could do is to bind the Source property of the Image in the template to some property that you can then set individually for each control to which the template is applied:

<Style TargetType="Button" x:Key="ButtonEllipse">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="Button">
                <StackPanel Orientation="Vertical">
                    <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0 0 0 10"/>
                    <Image x:Name="ButtonImage" HorizontalAlignment="Center" VerticalAlignment="Center" Stretch="None"
                                   Source="{Binding Tag, RelativeSource={RelativeSource AncestorType=Button}}"/>
                </StackPanel>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>
...
<Button Content="Test" Style="{StaticResource ButtonEllipse}" Tag="image.png" />
mm8
  • 163,881
  • 10
  • 57
  • 88
1

I would write a custom control that looks something like this:

internal class IconButton : Button
{
    public ImageSource Source
    {
        get { return (ImageSource)GetValue(SourceProperty); }
        set { SetValue(SourceProperty, value); }
    }

    public static readonly DependencyProperty SourceProperty =
        DependencyProperty.Register("Source", typeof(ImageSource), typeof(IconButton), new PropertyMetadata(null));

}

Then edit your style to accommodate it:

<Style TargetType="{x:Type location:IconButton}" x:Key="ButtonEllipse">
<Setter Property="Template">
    <Setter.Value>
        <ControlTemplate TargetType="{x:Type location:IconButton}">
            <StackPanel Orientation="Vertical">
                <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0 0 0 10"/>
                <Image x:Name="ButtonImage" HorizontalAlignment="Center" VerticalAlignment="Center" Stretch="None" Source="{TemplateBinding Source"/>
            </StackPanel>
        </ControlTemplate>
    </Setter.Value>
</Setter>

Where location is a xaml-defined namespace where the IconButton class is placed.

Then just set the Source property of your button. You can mess around with the Source property to set a default as well.

J R
  • 146
  • 1
  • 9
0

In order to bind a property to your style you should do the following:

Create a user control let's name it ButtonImage.cs

    public class ButtonImage : Button
{
    public ImageSource Source
    {
        get
        {
            return (ImageSource)GetValue(SourceProperty);
        }
        set
        {
            SetValue(SourceProperty, value);
        }
    }

    public static readonly DependencyProperty SourceProperty =
        DependencyProperty.Register("Source", 
            typeof(ImageSource), 
            typeof(ButtonImage), 
            new PropertyMetadata(null));

}

I would create a ResourceDictionary so you can use it for all your styles. Let's name it Dictionary.xaml. An there you define your style like:

        <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                        xmlns:local="clr-namespace:WpfApplication4">
        <Style TargetType="{x:Type local:ButtonImage}" x:Key="ButtonEllipse">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type local:ButtonImage}">
                        <StackPanel Orientation="Vertical">
                            <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0 0 0 10"/>
                            <Image x:Name="ButtonImage" HorizontalAlignment="Center" VerticalAlignment="Center" Stretch="None" Source="{TemplateBinding Source}"/>
                        </StackPanel>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </ResourceDictionary>

Then in your view you can do:

<Window x:Class="WpfApplication4.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:wpfApplication4="clr-namespace:WpfApplication4"
    mc:Ignorable="d"
    Title="MainWindow" Height="450" Width="800">
    <Window.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary 
                  Source="Dictionary.xaml">
                </ResourceDictionary>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Window.Resources>
    <Grid>
        <wpfApplication4:ButtonImage Margin="0,50,0,0" Content="Test" Source="{Binding Name}" Style="{StaticResource ButtonEllipse}" />
    </Grid>
</Window>