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" />
Related
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>
I am wondering if it is possible to associate Styles for certain controls with a custom window in WPF.
Here's the scenario - I have created a custom window, and have defined styles for a number of controls that I will use in this window. These are contained in a portable class library.
The catch is that I only want the controls to use the style from my library when they are used in the custom window (there are several different windows in the application).
I understand that I can assign the styles a key, and load them from my portable library in my application's app.xaml using pack syntax, for example:
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/Custom.Application.Library.Controls;component/Styles/CheckBox.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
And then add and style the control within my custom window as such:
<CheckBox x:Name="checkBox" Style="{StaticResource SpecialCheckBox}"
But what I would really like to do is define they styles in my class library without a key, as in this:
<Style TargetType="{x:Type CheckBox}">
Instead of this:
<Style x:Key="SpecialCheckBox" TargetType="{x:Type CheckBox}">
So that when this checkbox is used in my custom window it automatically inherits the style. If I define the style like this, and load it into my app.xaml, the problem is obviously that ALL checkboxes will inherit this style, not just checkboxes used in my custom window.
So, what I'm trying to find out is if there is any way to associate a style resource explicitly with a custom window, so that I can define the styles without a key, and have them by default inherit the "Special" style when used in my custom window, but use the WPF defaults in any other windows of the application. Does anyone have experience with this?
For clarity here is the code of my custom window:
XAML:
<!-- Window style -->
<Style TargetType="{x:Type Controls:CCTApplicationWindow}">
<Setter Property="WindowStyle" Value="None"/>
<Setter Property="AllowsTransparency" Value="True"/>
<Setter Property="ResizeMode" Value="CanResizeWithGrip"/>
<Setter Property="MinWidth" Value="500"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Controls:CCTApplicationWindow}">
<Border BorderBrush="#FF999999">
<Border.Style>
<Style TargetType="{x:Type Border}">
<Setter Property="BorderThickness" Value="1"/>
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=WindowState}" Value="Maximized">
<Setter Property="BorderThickness" Value="7"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<Grid>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="29"/>
<RowDefinition />
</Grid.RowDefinitions>
<Controls:CCTApplicationHeader Grid.Row="0"
Margin="0"
Title="{TemplateBinding Title}"
DragMoveCommand="{TemplateBinding DragMoveCommand}"
MaximizeCommand="{TemplateBinding MaximizeCommand}"
MinimizeCommand="{TemplateBinding MinimizeCommand}"
CloseCommand="{TemplateBinding CloseCommand}"/>
<Grid Background="White" Grid.Row="1" Margin="0">
<AdornerDecorator>
<ContentPresenter/>
</AdornerDecorator>
</Grid>
</Grid>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
CS:
public partial class CCTApplicationWindow : Window
{
public static readonly DependencyProperty MaximizeCommandProperty = DependencyProperty.Register("MaximizeCommand", typeof(DelegateCommand), typeof(CCTApplicationWindow));
public static readonly DependencyProperty MinimizeCommandProperty = DependencyProperty.Register("MinimizeCommand", typeof(DelegateCommand), typeof(CCTApplicationWindow));
public static readonly DependencyProperty CloseCommandProperty = DependencyProperty.Register("CloseCommand", typeof(DelegateCommand), typeof(CCTApplicationWindow));
public static readonly DependencyProperty DragMoveCommandProperty = DependencyProperty.Register("DragMoveCommand", typeof(DelegateCommand), typeof(CCTApplicationWindow));
public CCTApplicationWindow()
{
MaximizeCommand = new DelegateCommand(MaximizeExecute);
MinimizeCommand = new DelegateCommand(MinimizeExecute);
CloseCommand = new DelegateCommand(CloseExecute);
DragMoveCommand = new DelegateCommand(DragMoveExecute);
}
static CCTApplicationWindow()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(CCTApplicationWindow), new FrameworkPropertyMetadata(typeof(CCTApplicationWindow)));
}
public DelegateCommand MaximizeCommand
{
get
{
return (DelegateCommand)GetValue(MaximizeCommandProperty);
}
set
{
SetValue(MaximizeCommandProperty, value);
}
}
public DelegateCommand MinimizeCommand
{
get
{
return (DelegateCommand)GetValue(MinimizeCommandProperty);
}
set
{
SetValue(MinimizeCommandProperty, value);
}
}
public DelegateCommand CloseCommand
{
get
{
return (DelegateCommand)GetValue(CloseCommandProperty);
}
set
{
SetValue(CloseCommandProperty, value);
}
}
public DelegateCommand DragMoveCommand
{
get
{
return (DelegateCommand)GetValue(DragMoveCommandProperty);
}
set
{
SetValue(DragMoveCommandProperty, value);
}
}
private void MaximizeExecute(object obj)
{
if (this.WindowState != WindowState.Maximized)
{
this.WindowState = WindowState.Maximized;
}
else
{
SystemCommands.RestoreWindow(this);
}
}
private void MinimizeExecute(object obj)
{
SystemCommands.MinimizeWindow(this);
}
private void CloseExecute(object obj)
{
SystemCommands.CloseWindow(this);
}
private void DragMoveExecute(object obj)
{
DragMove();
}
}
Yes, you can do this, but you shouldn't! You've tagged this question as MVVM and yet your architecture design breaks MVVM entirely. The whole point of MVVM is that view logic is contained within the view model layer; your view models are the ones that should be keeping track of the logical hierarchy and they are the ones that should be exposing properties to the views to control their appearance. To put it another way, just because XAML is flexible enough and powerful enough to implement such logic doesn't mean it's the best place to actually do it!
To answer your question though, yes, this can be done with a DataTrigger binding to the parent with ObjectToTypeConverter. Here's an example of setting the TextBlock background to CornflowerBlue, unless its immediate parent is a Grid in which case it should be set to PaleGoldenrod:
<StackPanel Orientation="Vertical">
<StackPanel.Resources>
<converters:ObjectToTypeConverter x:Key="ObjectToTypeConverter" />
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Background" Value="CornflowerBlue" />
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Parent, RelativeSource={RelativeSource Mode=Self}, Converter={StaticResource ObjectToTypeConverter}}" Value="{x:Type Grid}">
<Setter Property="Background" Value="PaleGoldenrod" />
</DataTrigger>
</Style.Triggers>
</Style>
</StackPanel.Resources>
<Grid Width="100" Height="32" HorizontalAlignment="Left">
<TextBlock Text="TextBox A" /> <!-- Gets a PaleGoldenrod background -->
</Grid>
<Canvas Width="100" Height="32" HorizontalAlignment="Left">
<TextBlock Text="TextBox B" /> <!-- Gets a CornflowerBlue background -->
</Canvas>
</StackPanel>
And here's the converter code. It's worth pointing out that if you're happy to simply check that a parent of a given type exists somewhere in the hierarchy (as opposed to the immediate parent) then you don't even need this, you can just attempt to bind to RelativeSource with AncestorType set to the relevant parent type.
// based on http://stackoverflow.com/questions/8244658/binding-to-the-object-gettype
[ValueConversion(typeof(object), typeof(Type))]
public class ObjectToTypeConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value == null ? null : value.GetType();
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new InvalidOperationException();
}
}
But again, I implore you, if you really do want to adhere to MVVM then do not do it like this! This is exactly the kind of problem that "proper" MVVM was designed to solve.
Simplest way is to create a separate ResourceDictionary for your Custom window. And use it either using XAML or load it using Code.
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.
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.
General question. I have a ControlTemplate that is reasonably complex. Several TextBoxes etc.
I can't use TemplateBinding to bring all the properties to the surface so that I can set all the styles.
Is there a way for a Style to 'delv' into the controls within a control to set values?
Hope my question is clear without an example.
Thanks
The short answer is no. The ControlTemplate is essentially a black box, at least where XAML is concerned (there are ways to dig down the visual tree in code).
When you say you "can't use TemplateBinding", why not? If you just don't have enough available properties that can be fixed by creating some attached properties for the values you want to pass through. This is assuming you're templating a control that you can't change, otherwise you can just add new dependency properties.
Attached property:
public static class CustomProps
{
public static readonly DependencyProperty MyNewBrushProperty = DependencyProperty.RegisterAttached(
"MyNewBrush",
typeof(Brush),
typeof(CustomProps),
new UIPropertyMetadata(Brushes.Green));
public static Brush GetMyNewBrush(DependencyObject target)
{
return (Brush)target.GetValue(MyNewBrushProperty);
}
public static void SetMyNewBrush(DependencyObject target, Brush value)
{
target.SetValue(MyNewBrushProperty, value);
}
}
And usage in Style and Template:
<Style TargetType="{x:Type Button}">
<Setter Property="local:CustomProps.MyNewBrush" Value="Red" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Background="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(local:CustomProps.MyNewBrush)}">
<ContentPresenter/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Using this method also still allows overriding values on individual instances.