Style not getting applied first time - wpf

I am styling Grid Columns and there are some columns with custom template and some with default template.
The first time when I load the view the style is not applied to the columns with default template, but if I add/remove the columns in the grid (at runtime), I can see styling applied to all columns.
My code behind defines the following attached properties
public static readonly DependencyProperty GridLinesBorderBrushProperty = DependencyProperty.RegisterAttached("GridLinesBorderBrush", typeof(SolidColorBrush), typeof(CarbonBlotter), new PropertyMetadata(Brushes.Transparent));
public static readonly DependencyProperty GridLinesBorderThicknessProperty = DependencyProperty.RegisterAttached("GridLinesBorderThickness", typeof(Thickness), typeof(CarbonBlotter), new PropertyMetadata(new Thickness(0)));
public SolidColorBrush GetGridLinesBorderBrush(UIElement element_)
{
return (SolidColorBrush)element_.GetValue(GridLinesBorderBrushProperty);
}
public void SetGridLinesBorderBrush(UIElement element_, SolidColorBrush value_)
{
element_.SetValue(GridLinesBorderBrushProperty, value_);
}
public Thickness GetGridLinesBorderThickness(UIElement element_)
{
return (Thickness)element_.GetValue(GridLinesBorderThicknessProperty);
}
private void ShowGridLines()
{
UserSettings.GridLineType gridLineType = _userSettings.ShowGridLines;
Thickness gridLinesBorderThickness = new Thickness(0, 0, 1, 1);
if (gridLineType == UserSettings.GridLineType.Off)
{
SetGridLinesBorderThickness(_grid, new Thickness(0));
SetGridLinesBorderBrush(_grid, Brushes.Transparent);
SetAllowGridLines(_grid, false);
}
else (gridLineType == UserSettings.GridLineType.Black)
{
SetGridLinesBorderThickness(_grid, gridLinesBorderThickness);
SetGridLinesBorderBrush(_grid, Brushes.Black);
SetAllowGridLines(_grid, true);
}
}
And on my Xaml I have a default template
<Style TargetType="ig:CellValuePresenter" BasedOn="{StaticResource {x:Type ig:CellValuePresenter}}">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderThickness" Value="{Binding Path=DataPresenter.(pwc:CarbonBlotter.GridLinesBorderThickness), RelativeSource={RelativeSource Self}}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ig:CellValuePresenter}">
<igw:CardPanel>
<Border x:Name="MainBorder" CornerRadius="{TemplateBinding CornerRadius}" BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding BorderBrush}" Background="{TemplateBinding Background}" Padding="4"/>
...
And I have some custom templates which are based on default template
<Style x:Key="_columnStyle" TargetType="{x:Type ig:CellValuePresenter}" BasedOn="{StaticResource {x:Type ig:CellValuePresenter}}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border BorderBrush="{TemplateBinding BorderBrush}" Margin="{TemplateBinding Margin}" BorderThickness="{TemplateBinding BorderThickness}">
<pwc:BlotterCashTradingLanguageBar/>
</Border>
...
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
And depending on the column name a default or custom template will be applied. And I am finding that the first time I launch the view, the columns with default template have no border lines, but if I change the view by adding few columns, the view gets refreshed and all the columns have the gridlines.
It looks like the default template is not picking the value from attached properties when the view loads first time.
Any ideas.

Hmm, its weird, tried to set the attached properties from various places OnLoad, OnRender, from derived class using Property Override, nothing worked.
Created a new style with a key, made it based on default style. Where ever custom style is not availble used this new style. And it works.

Related

Add new brush property in VS WPF designer

I'm creating a new button control, and I wanted to create a new property under the brushes section where I can set the hovercolor. Anyone know how you would do this?
The simplest way to add a property that will appear in the "Brush" section of the Properties panel is to - define a Brush property in control's class code:
public partial class MyFancyControl : UserControl
{
// ...
public Brush FancyBrush
{
get;
set;
}
// ...
}
The property will show in "Brush" section with no further action (at least it does in my VS2013, see below).
While such a property will work just fine in most cases, the proper way to do it is to define it as a DependencyProperty:
public partial class MyFancyControl : UserControl
{
// ...
public Brush FancyBrush
{
get
{
return (Brush)GetValue(FancyBrushProperty);
}
set
{
SetValue(FancyBrushProperty, value);
}
}
public static readonly DependencyProperty FancyBrushProperty =
DependencyProperty.Register("FancyBrush", typeof(Brush), typeof(IntUpDown), new PropertyMetadata(default(Brush)));
// ...
}
Using DependencyProperty will enable binding and other "advanced" stuff.
Tip: Use VS Intellisense helpers to avoid need to type all the surrounding code - type "propdp" and press Tab twice.
To make sure the property will show up in the correct section of the Properties panel, add the Category attribute:
[System.ComponentModel.Category("Brush")]
public Brush FancyBrush
{...
Again, this seems to work automatically for Brush type so it may not be necessary.
You can also add a Description attribute that will show in the tooltip in the Properties panel:
[System.ComponentModel.Description("Gets or sets a brush that defines fancy look of the control.")]
You can just create the brush and add it as a resource something like:
<SolidColorBrush x:Key="MouseOverColor" Color="#FFFFFFF"/>
Then in the template triggers in your button template:
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="ButtonBorder" Property="Background" Value="StaticResource MouseOverColor}"/>
</Trigger>
Here is a simple button from in a resource dictionary file that uses defined brushes:
<SolidColorBrush x:Key="SelectionHighlightBrush" Color="#282828"/>
<SolidColorBrush x:Key="SelectionHighlightTextBrush" Color="White"/>
<SolidColorBrush x:Key="ForegroundBrush" Color="#282828"/>
<SolidColorBrush x:Key="ControlBackgroundBrush" Color="White"/>
<SolidColorBrush x:Key="ControlBorderBrush" Color="#C0C0C0" />
<Style TargetType="{x:Type Button}">
<Setter Property="Foreground" Value="{StaticResource ForegroundBrush}"/>
<Setter Property="Background" Value="{StaticResource ControlBackgroundBrush}"/>
<Setter Property="BorderBrush" Value="{StaticResource ControlBorderBrush}"></Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate x:Name="temp" TargetType="ToggleButton">
<Border x:Name="bd" CornerRadius="3"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<ContentPresenter x:Name="contentPresenter" Margin="{TemplateBinding Padding}"
ContentTemplate="{TemplateBinding ContentTemplate}"
Content="{TemplateBinding Content}"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="bd" Property="Background" Value="{StaticResource SelectionHighlightBrush}"/>
<Setter Property="TextElement.Foreground" Value="{StaticResource SelectionHighlightTextBrush}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

WPF: Making Properties of embedded control from template in custom control available

I have a Custom Control IconMD with the Properties IconName,OverlayName and OverlayPosition
I have embedded this Control in another Custom Control IconButton like so:
<ControlTemplate TargetType="{x:Type local:IconButton}">
<Border
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Border.Style>
<Style TargetType="{x:Type Border}">
<Setter Property="Opacity" Value="0.7"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Opacity" Value="1"/>
</Trigger>
</Style.Triggers>
</Style>
</Border.Style>
<Grid>
<local:IconMD
x:Name="_ButtonIcon"
OverlayPosition="{TemplateBinding OverlayPosition}"
IconName="{TemplateBinding IconName}"
OverlayName="{TemplateBinding OverlayIconName}"
/>
</Grid>
</Border>
</ControlTemplate>
The only purpose of the Dependency Properties OverlayPosition, IconName and OverlayIconName of this IconButton Control are simply to be passed through to the embedded Icon - the same idea as BorderBrush etc.
Now, as I also have IconToggleButton and IconRepeatButton (which inherit from the respective Base Classes and cannot inherit from IconButton!?), i must repeat this pattern for each of them. Should the capabilities of my IconMD Class grow, i would have to expand this pattern in every Custom Control that uses it.
How can I simply make the (properties of the) named IconMD Control "_ButtonIcon" available outside of my IconButton?
I would imagine instead of this
<imCC:IconButton
IconName="mdi-account-card-details"
OverlayIconName="mdi-multiplication-box"/>
writing something like this
<imCC:IconButton
_ButtonIcon.IconName="mdi-account-card-details"
_ButtonIcon.OverlayName="mdi-multiplication-box"/>
You can use attached properties instead of normal dependency properties (create in visual studio with snipped propa).
If you create IconName as attached property in class IconButton, you set it as follows:
<imCC:IconButton
imCC:IconButton.IconName="mdi-account-card-details"
...
And use in the ControlTemplate like this:
<local:IconMD
x:Name="_ButtonIcon"
IconName="{Binding Path=(local:IconButton.IconName),RelativeSource={RelativeSource TemplatedParent}}"
...
You could define the properties as attached properties that you can set on any UIElement or Button element.
Attached Properties Overview: https://learn.microsoft.com/en-us/dotnet/framework/wpf/advanced/attached-properties-overview
namespace WpfApplication1
{
public static class AttachedProperties
{
public static readonly DependencyProperty IsBubbleSourceProperty = DependencyProperty.RegisterAttached(
"IconName", typeof(string), typeof(AttachedProperties));
public static void SetIconName(UIElement element, string value)
{
element.SetValue(IsBubbleSourceProperty, value);
}
public static string GetIconName(UIElement element)
{
return (string)element.GetValue(IsBubbleSourceProperty);
}
}
}
<imCC:IconButton xmlns:local="clr-namespace:WpfApplication1"
local:AttachedProperties.IconName="mdi-account-card-details" />

How to interpret the style programmatically

In my application I have to manipulate a DataGrid in code-behind (The DataGrid is also created in code-behind in runtime), and I want to set below styles for the DataGrid
<DataGrid.RowHeaderStyle>
<Style TargetType="DataGridRowHeader">
<Setter Property="Visibility" Value="Collapsed"/>
<Setter Property="Template" Value="{x:Null}"/>
</Style>
</DataGrid.RowHeaderStyle>
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="DataGridRow">
<Border BorderThickness="{TemplateBinding Border.BorderThickness}" BorderBrush="{TemplateBinding Border.BorderBrush}" Background="{TemplateBinding Panel.Background}" Name="DGR_Border" SnapsToDevicePixels="True">
<SelectiveScrollingGrid> <!--How to translate this-->
<DataGridCellsPresenter ItemsPanel="{TemplateBinding ItemsControl.ItemsPanel}" SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}"/>
</SelectiveScrollingGrid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</DataGrid.RowStyle>
Below is the my interpreted code, but I do not know how to 'translate' the SelectiveScrollingGrid part
var myGrid = new DataGrid
{
RowHeaderStyle = new Style(typeof(DataGridRowHeader)),
RowStyle = new Style(typeof(DataGridRow))
};
myGrid.RowHeaderStyle.Setters.Add(new Setter(VisibilityProperty, Visibility.Collapsed));
myGrid.RowHeaderStyle.Setters.Add(new Setter(DataGridRowHeader.TemplateProperty, null));
ControlTemplate templateButton = new ControlTemplate(typeof(DataGridRow));
FrameworkElementFactory elemFactory = new FrameworkElementFactory(typeof(Border));
elemFactory.SetValue(Border.BorderThicknessProperty, new TemplateBindingExtension(Border.BorderThicknessProperty));
elemFactory.SetValue(Border.BorderBrushProperty, new TemplateBindingExtension(Border.BorderBrushProperty));
elemFactory.SetValue(Border.BackgroundProperty, new TemplateBindingExtension(Panel.BackgroundProperty));
elemFactory.SetValue(Border.NameProperty, "DGR_Border");
elemFactory.SetValue(Border.SnapsToDevicePixelsProperty, true);
var cellsPresenterFactory = new FrameworkElementFactory(typeof(DataGridCellsPresenter));
cellsPresenterFactory.SetValue(DataGridCellsPresenter.ItemsPanelProperty, new TemplateBindingExtension(ItemsControl.ItemsPanelProperty));
cellsPresenterFactory.SetValue(DataGridCellsPresenter.SnapsToDevicePixelsProperty, new TemplateBindingExtension(UIElement.SnapsToDevicePixelsProperty));
//elemFactory.AppendChild(selectiveScrollingGridFactory);
templateButton.VisualTree = elemFactory;
elemFactory.AppendChild(new FrameworkElementFactory(typeof(ContentPresenter)));
Just create another FrameworkElementFactory with a type of System.Windows.Controls.Primitives.SelectiveScrollingGrid:
...
var selectiveScrollingGridFactory = new FrameworkElementFactory(typeof(System.Windows.Controls.Primitives.SelectiveScrollingGrid));
elemFactory.AppendChild(selectiveScrollingGridFactory);
var cellsPresenterFactory = new FrameworkElementFactory(typeof(DataGridCellsPresenter));
cellsPresenterFactory.SetValue(DataGridCellsPresenter.ItemsPanelProperty, new TemplateBindingExtension(ItemsControl.ItemsPanelProperty));
cellsPresenterFactory.SetValue(DataGridCellsPresenter.SnapsToDevicePixelsProperty, new TemplateBindingExtension(UIElement.SnapsToDevicePixelsProperty));
selectiveScrollingGridFactory.AppendChild(selectiveScrollingGridFactory);
...
Note that the recommended way to programmatically create a template is to load XAML from a string or a memory stream using the Load method of the XamlReader class as stated in the documenation on MSDN: https://msdn.microsoft.com/en-us/library/system.windows.frameworkelementfactory(v=vs.110).aspx

Hide legend of WPF Toolkit chart with more than one data series

I am trying to use charts from the WPF Toolkit (with LineSeries) and I don't want a legend at all. I need this since I have 10 such charts each with data from a different source and I would like to draw one legend for all 10, to save screen real estate.
By default the legend appears the moment you add a second LineSeries. Is there any way to prevent it from even appearing?
Thanks,
sprite.
There doesn't seem to be an especially clean way. One simple approach is to set the Legend's Width to zero using LegendStyle:
<charting:Chart>
<charting:Chart.LegendStyle>
<Style TargetType="datavis:Legend">
<Setter Property="Width" Value="0" />
</Style>
</charting:Chart.LegendStyle>
A more drastic approach is to replace the ControlTemplate with one that does not include a Legend:
<charting:Chart>
<charting:Chart.Template>
<ControlTemplate TargetType="{x:Type charting:Chart}">
<Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<datavis:Title Content="{TemplateBinding Title}" Style="{TemplateBinding TitleStyle}" />
<chartingprimitives:EdgePanel Name="ChartArea" Style="{TemplateBinding ChartAreaStyle}" Grid.Row="1" Margin="0,15,0,15">
<Grid Panel.ZIndex="-1" Style="{TemplateBinding PlotAreaStyle}" />
<Border Panel.ZIndex="10" BorderBrush="#FF919191" BorderThickness="1" />
</chartingprimitives:EdgePanel>
</Grid>
</Border>
</ControlTemplate>
</charting:Chart.Template>
Use following namespaces:
xmlns:charting="clr-namespace:System.Windows.Controls.DataVisualization.Charting;assembly=System.Windows.Controls.DataVisualization.Toolkit"
xmlns:datavis="clr-namespace:System.Windows.Controls.DataVisualization;assembly=System.Windows.Controls.DataVisualization.Toolkit"
xmlns:chartingprimitives="clr-namespace:System.Windows.Controls.DataVisualization.Charting.Primitives;assembly=System.Windows.Controls.DataVisualization.Toolkit"
I tried Quarermeister's approach but his has a reference to a "datavis" assembly in the TargetType attribute that I didn't have.
<chartingToolkit:Chart.LegendStyle>
<Style TargetType="Control">
<Setter Property="Width" Value="0" />
<Setter Property="Height" Value="0" />
</Style>
</chartingToolkit:Chart.LegendStyle>
I also had to add padding to the right side of the chart because without the legend, my x-axis interval labels were extending outside the chart area.
Much more sensible approach...
<charting:LineSeries.LegendItemStyle >
<Style TargetType="{x:Type charting:LegendItem}">
<Setter Property="Visibility" Value="Collapsed"/>
</Style>
</charting:LineSeries.LegendItemStyle>
Worked better for me than setting values to 0...
Cheers!
Attached Property for DRY, easy usage:
<charting:Chart helpers:ChartHelpers.IsLegendHidden="True" ...
public static class ChartHelpers
{
static ChartHelpers()
{
HideLegendStyle = new Style(typeof(Legend));
HideLegendStyle.Setters.Add(new Setter(Legend.WidthProperty, 0.0));
HideLegendStyle.Setters.Add(new Setter(Legend.HeightProperty, 0.0));
HideLegendStyle.Setters.Add(new Setter(Legend.VisibilityProperty, Visibility.Collapsed));
}
/// <summary>Gets a <see cref="Style"/> to hide the legend.</summary>
public static readonly Style HideLegendStyle;
#region IsLegendHidden
[Category("Common")]
[AttachedPropertyBrowsableForType(typeof(Chart))]
public static bool GetIsLegendHidden(Chart chart)
{
return (bool)chart.GetValue(IsLegendHiddenProperty);
}
public static void SetIsLegendHidden(Chart chart, bool value)
{
chart.SetValue(IsLegendHiddenProperty, value);
}
public static readonly DependencyProperty IsLegendHiddenProperty =
DependencyProperty.RegisterAttached(
"IsLegendHidden",
typeof(bool), // type
typeof(ChartHelpers), // containing static class
new PropertyMetadata(default(bool), OnIsLegendHiddenChanged)
);
private static void OnIsLegendHiddenChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
OnIsLegendHiddenChanged((Chart)d, (bool)e.NewValue);
}
private static void OnIsLegendHiddenChanged(Chart chart, bool isHidden)
{
if (isHidden)
{
chart.LegendStyle = HideLegendStyle;
}
}
#endregion IsLegendHidden
}

WPF TriState Image Button

Does anyone have any pointers for creating a tristate image button?
I have the following but what I really want to do is have a control with multiple ImageSource properties like <Controls.TristateButton Image="" HoverImage="" PressedImage="" />
<Style TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<StackPanel Orientation="Horizontal" >
<Image Name="PART_Image" Source="path to normal image" />
</StackPanel>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Source" Value="path to mouse over image" TargetName="PART_Image"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Source" Value="path to pressed image" TargetName="PART_Image"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I have run into the same problem myself. I have created an open source project here http://imagebuttonwpf.codeplex.com where you can get the latest version of the Image Button.
I don't like the "accepted" solution provided for several reasons (Although it is a lighter solution and has its own advantages)
Blockquote The accepted answer to this StackOverflow question shows an easy way to do this: WPF - How to create image button with template
Mainly I don't think its correct to override the control template for every button you would like to change the image for so I have created a custom control called ImageButton. It extends from button so as to have any of its functionality (though it may be able to extend from content control just as easily) but also contains an Image which can be styled without rewriting the entire control template.
Another reason why I don't like rewriting the entire control template for my button each time is that my base button style contains several borders and animation effects for mouse over, is pressed etc. Rewriting these each time obviously has its own redundancy problems.
Anyway here is the ImageButton class
public class ImageButton : Button
{
static ImageButton() {
DefaultStyleKeyProperty.OverrideMetadata(typeof(ImageButton), new FrameworkPropertyMetadata(typeof(ImageButton)));
}
#region Dependency Properties
public double ImageSize
{
get { return (double)GetValue(ImageSizeProperty); }
set { SetValue(ImageSizeProperty, value); }
}
public static readonly DependencyProperty ImageSizeProperty =
DependencyProperty.Register("ImageSize", typeof(double), typeof(ImageButton),
new FrameworkPropertyMetadata(30.0, FrameworkPropertyMetadataOptions.AffectsRender, ImageSourceChanged));
public string NormalImage
{
get { return (string)GetValue(NormalImageProperty); }
set { SetValue(NormalImageProperty, value); }
}
public static readonly DependencyProperty NormalImageProperty =
DependencyProperty.Register("NormalImage", typeof(string), typeof(ImageButton),
new FrameworkPropertyMetadata("", FrameworkPropertyMetadataOptions.AffectsRender,ImageSourceChanged));
public string HoverImage
{
get { return (string)GetValue(HoverImageProperty); }
set { SetValue(HoverImageProperty, value); }
}
public static readonly DependencyProperty HoverImageProperty =
DependencyProperty.Register("HoverImage", typeof(string), typeof(ImageButton),
new FrameworkPropertyMetadata("", FrameworkPropertyMetadataOptions.AffectsRender, ImageSourceChanged));
public string PressedImage
{
get { return (string)GetValue(PressedImageProperty); }
set { SetValue(PressedImageProperty, value); }
}
public static readonly DependencyProperty PressedImageProperty =
DependencyProperty.Register("PressedImage", typeof(string), typeof(ImageButton),
new FrameworkPropertyMetadata("", FrameworkPropertyMetadataOptions.AffectsRender, ImageSourceChanged));
public string DisabledImage
{
get { return (string)GetValue(DisabledImageProperty); }
set { SetValue(DisabledImageProperty, value); }
}
public static readonly DependencyProperty DisabledImageProperty =
DependencyProperty.Register("DisabledImage", typeof(string), typeof(ImageButton),
new FrameworkPropertyMetadata("", FrameworkPropertyMetadataOptions.AffectsRender, ImageSourceChanged));
private static void ImageSourceChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
Application.GetResourceStream(new Uri("pack://application:,,," + (string) e.NewValue));
}
#endregion
Next up we need to provide a default control template for our button ive taken most of my borders etc out of this one, bar one so you can see that it is inherited throughout all our styles
<ControlTemplate x:Key="ImageButtonTemplate" TargetType="{x:Type Controls:ImageButton}">
<Grid x:Name="Grid">
<Border x:Name="Background" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="3" />
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Center">
<Image x:Name="ButtonImage" Source="{Binding NormalImage, RelativeSource={RelativeSource TemplatedParent}}"
Height="{Binding ImageSize, RelativeSource={RelativeSource TemplatedParent}}"
Width="{Binding ImageSize, RelativeSource={RelativeSource TemplatedParent}}"/>
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" RecognizesAccessKey="True" />
</StackPanel>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter TargetName="ButtonImage" Property="Source" Value="{Binding HoverImage, RelativeSource={RelativeSource TemplatedParent}}" />
</Trigger>
<Trigger Property="IsPressed" Value="true">
<Setter TargetName="ButtonImage" Property="Source" Value="{Binding PressedImage, RelativeSource={RelativeSource TemplatedParent}}" />
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter TargetName="ButtonImage" Property="Source" Value="{Binding DisabledImage, RelativeSource={RelativeSource TemplatedParent}}" />
</Trigger>
</ControlTemplate.Triggers>
then of course we need a default style for our new image button
<Style TargetType="{x:Type Controls:ImageButton}" BasedOn="{x:Null}">
<Setter Property="Template" Value="{StaticResource ImageButtonTemplate}" />
</Style>
And of course the benefits of using this method i have created a style based on the parent style which uses a Setter to change the dependency properties (instead of needed to override the control template - the goal)
<Style x:Key="TestImageButton" TargetType="{x:Type Controls:ImageButton}" BasedOn="{StaticResource {x:Type Controls:ImageButton}}">
<Setter Property="NormalImage" Value="/ImageButton;component/Resources/clear.png"/>
<Setter Property="HoverImage" Value="/ImageButton;component/Resources/clear_green.png" />
<Setter Property="PressedImage" Value="/ImageButton;component/Resources/clear_darkgreen.png" />
<Setter Property="DisabledImage" Value="/ImageButton;component/Resources/clear_grey.png" />
</Style>
and finally this means that one can declare the button in a few different ways either declare the image path in the XAML
<Controls:ImageButton
Content="Test Button 1"
NormalImage="/ImageButton;component/Resources/edit.png"
HoverImage="/ImageButton;component/Resources/edit_black.png"
PressedImage="/ImageButton;component/Resources/edit_darkgrey.png"
DisabledImage="/ImageButton;component/Resources/edit_grey.png"/>
Or alternatively use the style
<Controls:ImageButton
Content="Test Button 2"
Style="{DynamicResource TestImageButton}"/>
Hope it helps
The accepted answer to this StackOverflow question shows an easy way to do this:
WPF - How to create image button with template
You create property triggers on the IsEnabled and IsPressed properties and show or hide the images as needed.
As Avanka noted in his answer, you'll need to create dependency properties to set the paths to the images.
Ideally, you have to create a custom control, inherited from Button. Add three dependency properties, and create default style for new control.
You can check ImageButton class from FluidKit library - it does exactly what you want.

Resources