WPF TriState Image Button - wpf

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.

Related

sidebar with materialIcons

I have a custom control, that I mad, for hosting the name, and icon of the panel, and made triggers, so it will change colors when I hover the icon or select it
public class NavButton : ListBoxItem {
static NavButton() {
DefaultStyleKeyProperty.OverrideMetadata(typeof(NavButton),
new FrameworkPropertyMetadata(typeof(NavButton)));
}
public Uri NavLink {
get { return (Uri)GetValue(NavLinkProperty); }
set { SetValue(NavLinkProperty, value); }
}
// This property will hold specifics on where to navigate within our appreciation
public static readonly DependencyProperty NavLinkProperty =
DependencyProperty.Register(
"NavLink", typeof(Uri),
typeof(NavButton),
new PropertyMetadata(null));
// This property will hold specifics on the icon
public Label Icon {
get { return (Label)GetValue(IconProperty); }
set { SetValue(IconProperty, value); }
}
// Using a DependencyProperty as the backing store for Icon. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IconProperty =
DependencyProperty.Register("Icon",
typeof(Label),
typeof(NavButton),
new PropertyMetadata(null));
The problem is, that whenever I call this control in my main, I always get this error
I am calling it like this
<custom:NavButton
Padding="6"
FontFamily="{StaticResource Material}"
Icon="{x:Static fonts:IconFont.VolumeHigh}"/>
And this is the recourse dictionary for the label
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<FontFamily x:Key="Material">/Fonts/Material.ttf#Material Design Icons</FontFamily>
</ResourceDictionary>
<Style TargetType="{x:Type local:NavButton}">
<Setter Property="HorizontalAlignment" Value="Center" />
<Setter Property="Cursor" Value="Hand" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:NavButton}">
<Border
x:Name="back"
Padding="{TemplateBinding Padding}"
Background="Transparent"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="8">
<Label
x:Name="icon"
Content="{TemplateBinding Icon}"
FontFamily="{StaticResource Material}"
FontSize="48"
Foreground="#7b8793" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="back" Property="Background" Value="#d0ebff" />
<Setter TargetName="icon" Property="Foreground" Value="#2a84f1" />
</Trigger>
<Trigger Property="IsSelected" Value="True">
<Setter TargetName="back" Property="Background" Value="#d0ebff" />
<Setter TargetName="icon" Property="Foreground" Value="#2a84f1" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
error message clearly says, that you have a type mismatch. Icon DP should not have type Label. it should have the same type as IconFont.VolumeHigh. or it could simple be object:
// This property will hold specifics on the icon
public object Icon {
get { return (Label)GetValue(IconProperty); }
set { SetValue(IconProperty, value); }
}
// Using a DependencyProperty as the backing store for Icon. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IconProperty =
DependencyProperty.Register("Icon",
typeof(object),
typeof(NavButton),
new PropertyMetadata(null));

how to create a generic background image binding from a control template?

i have button with this style:
<Style TargetType="Button" x:Key="btnStyle">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Image x:Name="img" Source="{TemplateBinding Tag}" Margin="{TemplateBinding Margin}"
Stretch="None"></Image>
<ControlTemplate.Triggers>
<Trigger Property="IsPressed" Value="true">
<Setter TargetName="img" Property="Source" Value="{DynamicResource imgClose_P}"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="true">
<Setter TargetName="img" Property="Source" Value="{DynamicResource imgClose_H}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
As you can see i'm binding the ImageSource to the Tag property of the button.
And in the Tag property i'm binding it to a ResourceDictionary that store this bitmap:
<BitmapImage x:Key="imgClose_N" UriSource="..\AppImages\mainWindow\TopBanner\CloseButton_sN.png" />
this gives me the ability to use this "Imagebutton" all over the application with different background images and one template.
the problem is how to keep this generic approach with triggers?
i would like the IsMouseOver trigger to change the background image but to bind it to some property of the control and not to write it hard coded in the control template.
how can this be done ?
As you already suggest by calling it an "Imagebutton", you may derive from Button and define some image properties to bind to, e.g. BackgroundImage, MouseOverImage, etc.
An alternative to deriving from Button would be to use attached properties to set the images and bind to those in your style, but these attached properties would also have to be defined somewhere, which doesn't make it simpler.
Here's an example for the first solution:
public class ImageButton : Button
{
public static readonly DependencyProperty NormalBackgroundImageProperty = DependencyProperty.Register(
"NormalBackgroundImage", typeof(ImageSource), typeof(ImageButton));
public static readonly DependencyProperty MouseOverBackgroundImageProperty = DependencyProperty.Register(
"MouseOverBackgroundImage", typeof(ImageSource), typeof(ImageButton));
public static readonly DependencyProperty PressedBackgroundImageProperty = DependencyProperty.Register(
"PressedBackgroundImage", typeof(ImageSource), typeof(ImageButton));
public ImageSource NormalBackgroundImage
{
get { return (ImageSource)GetValue(NormalBackgroundImageProperty); }
set { SetValue(NormalBackgroundImageProperty, value); }
}
public ImageSource MouseOverBackgroundImage
{
get { return (ImageSource)GetValue(MouseOverBackgroundImageProperty); }
set { SetValue(MouseOverBackgroundImageProperty, value); }
}
public ImageSource PressedBackgroundImage
{
get { return (ImageSource)GetValue(PressedBackgroundImageProperty); }
set { SetValue(PressedBackgroundImageProperty, value); }
}
}
and an appropriate style below. Note that this style also has a ContentPresenter for the button's content, and that it uses regular bindings with RelativeSource={RelativeSource TemplatedParent} instead of TemplateBindings. These are evaluated at runtime.
<Style TargetType="local:ImageButton">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:ImageButton">
<Grid Margin="{TemplateBinding Margin}">
<Image x:Name="img" Source="{Binding NormalBackgroundImage, RelativeSource={RelativeSource TemplatedParent}}" Stretch="None"/>
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter TargetName="img" Property="Source" Value="{Binding MouseOverBackgroundImage, RelativeSource={RelativeSource TemplatedParent}}"/>
</Trigger>
<Trigger Property="IsPressed" Value="true">
<Setter TargetName="img" Property="Source" Value="{Binding PressedBackgroundImage, RelativeSource={RelativeSource TemplatedParent}}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
You would have to define or replace the XAML namespace local with a mapping to the namespace/assembly that contains the class ImageButton.
The button could then be use like this:
<local:ImageButton
Margin="10"
NormalBackgroundImage="C:\Users\Public\Pictures\Sample Pictures\Desert.jpg"
PressedBackgroundImage="C:\Users\Public\Pictures\Sample Pictures\Tulips.jpg"
MouseOverBackgroundImage="C:\Users\Public\Pictures\Sample Pictures\Penguins.jpg"
Content="Click Me"/>

How turn-off tooltips for the whole application

Is it possible to turn-off toooltips for all controls (always or based on some rule) without setting TooltipService.IsEnabled on each control? I mean, going through all logical items takes too much time.
Try this. It hides all tooltips.
<Style TargetType="{x:Type ToolTip}">
<Setter Property="Visibility"
Value="Collapsed" />
</Style>
There are several ways you should be able to use to accomplish this. Marco Zhou outlines two of them in this posting., both of these methods relying on setting TooltipService.IsEnabled to False for a parent control such as a Window. Apparently it inherits to all children, so you can set it just there to disable all tooltips.
You could also set all of your Tooltips to a style which had bindings to a property that would make them invisible or disabled when you wanted.
EDIT
Adding the Code to make it easier to understand:
Create the ToolTipEnabled Attached Property which sets the FrameworkPropertyMetadataOptions.Inherits so that it will be inherited by the children.
public class ToolTipBehavior
{
public static Boolean GetIsToolTipEnabled(FrameworkElement obj)
{
return (Boolean)obj.GetValue(ToolTipEnabledProperty);
}
public static void SetToolTipEnabled(FrameworkElement obj, Boolean value)
{
obj.SetValue(ToolTipEnabledProperty, value);
}
public static readonly DependencyProperty ToolTipEnabledProperty = DependencyProperty.RegisterAttached(
"IsToolTipEnabled",
typeof(Boolean),
typeof(ToolTipBehavior),
new FrameworkPropertyMetadata(true, FrameworkPropertyMetadataOptions.Inherits, (sender, e) =>
{
FrameworkElement element = sender as FrameworkElement;
if (element != null)
{
element.SetValue(ToolTipService.IsEnabledProperty, e.NewValue);
}
}));
}
You can either use this property in the XAML or codebehind as below:
<Window x:Class="AnswerHarness.ToggleToolTipsDemo"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:cc="clr-namespace:AnswerHarness"
Title="ToggleToolTipsDemo" Height="300" Width="300" Name="window">
<StackPanel>
<CheckBox IsChecked="{Binding Path=(cc:ToolTipBehavior.IsToolTipEnabled), ElementName=window}" Content="Enable ToolTip"/>
<Border BorderBrush="Green" BorderThickness="1" Background="Yellow" ToolTip="Border">
<StackPanel>
<Button Width="120" Height="30" Content="Button1" ToolTip="Button1"/>
<Button Width="120" Height="30" Content="Button2" ToolTip="Button2"/>
<Button Width="120" Height="30" Content="Button3" ToolTip="Button3"/>
</StackPanel>
</Border>
</StackPanel>
</Window>
Or
public partial class ToggleToolTipsDemo : Window
{
public ToggleToolTipsDemo()
{
InitializeComponent();
// You can programmatically disable tool tip here.
this.SetValue(ToolTipBehavior.ToolTipEnabledProperty, false);
}
}
Put this style where it is accessible throughout the application(a resourcedictionary or App.xaml) so you won't need to reference this style in any textbox.
<Style BasedOn="{x:Null}" TargetType="{x:Type TextBox}">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
<Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"/>
<Setter Property="BorderBrush" Value="{StaticResource TextBoxBorder}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Padding" Value="1"/>
<Setter Property="AllowDrop" Value="true"/>
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="ScrollViewer.PanningMode" Value="VerticalFirst"/>
<Setter Property="Stylus.IsFlicksEnabled" Value="False"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBox}">
<Microsoft_Windows_Themes:ListBoxChrome x:Name="Bd" ToolTipService.IsEnabled="False" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" RenderMouseOver="{TemplateBinding IsMouseOver}" RenderFocused="{TemplateBinding IsKeyboardFocusWithin}" SnapsToDevicePixels="true">
<ScrollViewer ToolTipService.IsEnabled="False" x:Name="PART_ContentHost" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</Microsoft_Windows_Themes:ListBoxChrome>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
</Trigger>
<Trigger Property="Text" Value="">
<Setter Property="ToolTipService.IsEnabled" Value="False"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
NOTE
This is the default textbox style generated by Expression blend to which I have added the following trigger which enables tooltips when textbox is not empty and disables them otherwise
<Trigger Property="Text" Value="">
<Setter Property="ToolTipService.IsEnabled" Value="False"/>
</Trigger>
I don't know of any global setting, but there is an easy way to 'visit' all of the elements of your visual tree using Linq-to-VisualTree, I utility I wrote a while back that providers a Linq-to-XML style API for the visual tree.
The following should do the trick:
foreach(var element in window.Descendants())
ToolttipService.SetIsEnabled(element, false);
You can try to use
ToolTipService.IsOpenProperty.OverrideMetadata(typeof(DependencyObject),new PropertyMetadata(false));
I don't have an answer for handling the entire app in one statement, but I've been able to centralize a number of UI-specific parameters in a general base class, then create applications which are derived off this base class and inherit the centralized settings. I should mention there's some extra plumbing you have to add to the base class to support MVVM as in the following:
public class MyMainWindowBaseClass : Window, INotifyPropertyChanged
{
...whatever unrelated stuff you need in your class here...
private int m_toolTipDuration = 3000; // Default to 3 seconds
public int MyToolTipDuration
{
get { return m_toolTipDuration; }
set
{
if (m_toolTipDuration != value)
{
bool transition = (value == 0 || m_toolTipDuration == 0);
m_toolTipDuration = value;
NotifyPropertyChanged("MyToolTipDuration");
if (transition)
{
NotifyPropertyChanged("MyToolTipEnabled");
}
}
}
}
public bool MyToolTipEnabled
{
get { return (m_toolTipDuration > 0); }
}
public event PropertyChangedEventHandler PropertyChanged;
... whatever variables, properties, methods, etc., you need here...
///-----------------------------------------------------------------------------
/// <summary>
/// Fires property-changed event notification
/// </summary>
/// <param name="propertyName">The name of the property that changed</param>
///-----------------------------------------------------------------------------
public void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
The XAML code looks like this:
<Button Command="{Binding StartCommand}"
Content="Start"
FontWeight="Bold"
Height="Auto"
HorizontalAlignment="Left"
Margin="20,40,0,0"
Name="ui_StartButton"
ToolTip="Click this button to begin processing."
ToolTipService.IsEnabled="{Binding RelativeSource={RelativeSource AncestorType=Window},Path=MyToolTipEnabled}"
ToolTipService.ShowDuration="{Binding RelativeSource={RelativeSource AncestorType=Window},Path=MyToolTipDuration}"
VerticalAlignment="Top"
Width="90"/>
With the important bindings being those related to ToolTipService.IsEnabled and ToolTipService.ShowDuration.
You can see that if MyToolTipDuration is set to zero, MyToolTipEnabled will return false and this disables the tooltip. In my first attempt I tried simply setting MyToolTipDuration to zero without using the ToolTipService.IsEnabled= in conjunction with the MyToolTipEnabled property, but all that accomplished was flashing, barely-readable tooltips which appear and disappear.
Overall this worked pretty well for me (ymmv), though not as well as a single setting or single call that would have handled the entire app and circumvented the need for distributing these bindings into every item with a tooltip I wanted to be support the ability to disable. Oh well, when in Rome....
In any event, hopefully someone finds this of use.

WPF : give me a best way for icon button

We can easily make an icon button using a control template like the following code:
<Style x:Key="IconButton" TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid>
<Image x:Name="Background" Source="/UOC;component/TOOLBAR_BUTTON_NORMAL.png"/>
<Image Source="/UOC;component/ICON_SLICER.gif" Width="20" Height="20" Margin="0,-10,0,0"/>
<TextBlock Foreground="White" FontSize="9" Text="{TemplateBinding Button.Content}" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0,15,0,0"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="Button.IsMouseOver" Value="True">
<Setter Property="Source" TargetName="Background" Value="/UOC;component/TOOLBAR_BUTTON_OVER.png"/>
<Setter Property="Cursor" Value="Hand"/>
</Trigger>
<Trigger Property="Button.IsPressed" Value="True">
<Setter Property="Source" TargetName="Background" Value="/UOC;component/TOOLBAR_BUTTON_CLICK.png"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
But i think it's not a productive way in practice. because i can't make a number of styles for each one of icon buttons. (ex. let's assume three buttons in App:'open' button, 'close' button and 'navigate' button. these buttons have different icon sets. i can't make styles like 'IconButton_Close', 'IconButton_Open', 'IconButton_Nav'. it's too stupid.)
UserControl may be an answer. but i think it's not a smart way for that. because if i make UserControl, it'll be just a wrapper of the Button control. it's not a right way.
So, give me the best way for icon button.
thanks.
The correct way to do this would be to define a custom button class, like so:
public class MyButton : Button
{
static MyButton()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MyButton), new FrameworkPropertyMetadata(typeof(MyButton)));
}
public static readonly DependencyProperty ImageSourceProperty = DependencyProperty.Register("ImageSource", typeof(ImageSource),
typeof(MyButton), new FrameworkPropertyMetadata(null));
public ImageSource ImageSource
{
get { return (ImageSource)GetValue(ImageSourceProperty); }
set { SetValue(ImageSourceProperty, value); }
}
public static readonly DependencyProperty ImageSourceHoverProperty = DependencyProperty.Register("ImageSourceHover", typeof(ImageSource),
typeof(MyButton), new FrameworkPropertyMetadata(null));
public ImageSource ImageSourceHover
{
get { return (ImageSource)GetValue(ImageSourceHoverProperty); }
set { SetValue(ImageSourceHoverProperty, value); }
}
public static readonly DependencyProperty ImageSourcePressedProperty = DependencyProperty.Register("ImageSourcePressed", typeof(ImageSource),
typeof(MyButton), new FrameworkPropertyMetadata(null));
public ImageSource ImageSourcePressed
{
get { return (ImageSource)GetValue(ImageSourcePressedProperty); }
set { SetValue(ImageSourcePressedProperty, value); }
}
}
Then define the default Style like so:
<Style x:Key="{x:Type local:MyButton}" TargetType="{x:Type local:MyButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:MyButton}">
<Grid>
<Image x:Name="Background" Source="{TemplateBinding ImageSource}" />
<Image Source="/UOC;component/ICON_SLICER.gif" Width="20" Height="20" Margin="0,-10,0,0"/>
<TextBlock Foreground="White" FontSize="9" Text="{TemplateBinding Button.Content}" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0,15,0,0"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="Button.IsMouseOver" Value="True">
<Setter Property="Source" TargetName="Background" Value="{TemplateBinding ImageSourceHover}"/>
<Setter Property="Cursor" Value="Hand"/>
</Trigger>
<Trigger Property="Button.IsPressed" Value="True">
<Setter Property="Source" TargetName="Background" Value="{TemplateBinding ImageSourcePressed}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
And you'd use it like so:
<local:MyButton ImageSource="/UOC;component/TOOLBAR_BUTTON_NORMAL.png"
ImageSourceHover="/UOC;component/TOOLBAR_BUTTON_OVER.png"
ImageSourcePressed="/UOC;component/TOOLBAR_BUTTON_CLICK.png" />
I did something similar to this for a custom control a while back using the TemplatePart attribute. This displays an icon and some text in a panel. If the icons is the error or fail icon, it turns the text red. There is a dependency property called "Type" which is really just the image file name without the extension. Here's the code, I bet you can adapt this for a custom Button where you can set the source and still have your customization to the template.
[TemplatePart(Name = "PART_Image", Type = typeof(Image))]
public class IconPanel : ContentControl
{
static IconPanel()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(IconPanel), new FrameworkPropertyMetadata(typeof(IconPanel)));
}
public string Type
{
get { return (string)GetValue(TypeProperty); }
set { SetValue(TypeProperty, value); }
}
public static readonly DependencyProperty TypeProperty =
DependencyProperty.Register("Type", typeof(string), typeof(IconPanel),
new UIPropertyMetadata("warning", TypeChangedCallback));
static void TypeChangedCallback(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
IconPanel panel = obj as IconPanel;
panel.UpdateImage();
}
void UpdateImage()
{
Image img = GetTemplateChild("PART_Image") as Image;
if (img == null) return;
string ImagePath = String.Format("pack://application:,,,/Resources/{0}.png", this.Type);
Uri uri = new Uri(ImagePath, UriKind.RelativeOrAbsolute);
BitmapImage bmp = new BitmapImage(uri);
img.Source = bmp;
if ( String.Compare(Type, "error", true) == 0 ||
String.Compare(Type, "fail", true) == 0 )
{
this.Foreground = new SolidColorBrush(Color.FromRgb(0xFF, 0x00, 0x00));
}
}
public override void OnApplyTemplate()
{
UpdateImage();
base.OnApplyTemplate();
}
}
XAML:
<Style TargetType="{x:Type local:IconPanel}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:IconPanel}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Padding="7">
<Grid Background="{TemplateBinding Background}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Image
x:Name="PART_Image"
Margin="0,0,5,5"
VerticalAlignment="Top"
HorizontalAlignment="Left"
Width="16"
Height="16" />
<ContentPresenter Grid.Column="1"/>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

Cannot change Background color with triggers in XAML

I have a little problem and I don't know how can I fix it ...
I created a CustomControl called "StandardKeyView" from a Button. This control has a dependency property "DownImage" which is used to define a Background Image to my control during the mouse over.
Here's the definition of the DownImage property :
public SolidColorBrush DownImage
{
get { return (SolidColorBrush)GetValue( DownImageProperty ); }
set { SetValue( DownImageProperty, value ); }
}
public static readonly DependencyProperty DownImageProperty =
DependencyProperty.Register( "DownImage", typeof( SolidColorBrush ), typeof( StandardKeyView ) );
Here's the definition of a new StandardKeyView :
<skv:StandardKeyView Background="White" DownImage="Black"/>
Here's the default control template of a StandardKeyView :
<ControlTemplate TargetType="{x:Type skv:StandardKeyView}" x:Key="DefaultStandardKeyViewTemplate">
<Button Width="{TemplateBinding Width}" Height="{TemplateBinding Height}" Background="{TemplateBinding Background}" x:Name="MainButton" />
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="MainButton" Property="Background" Value="{TemplateBinding Property=DownImage}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
I think it should work fine, but not really. I have a weird error : when I try to start the application, I have a XamlParseException who said
Unable to convert the value of attribute 'Value' as an object of type ''.
I think the type of DownImage is good, then why doesn't it work when it used in the trigger?
Try using instead:
Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=DownImage}"

Resources