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));
Related
We've got a few Custom Controls with their own styles, templates. Basically something like that: (I'll post the full styles/templates at the end)
public class CustomBtn : Button
{
public propdp DependencyProp1...
public propdp DependencyProp2...
...
}
<Style x:Key="XStyle.CustomBtnStyle" TargetType="{x:Type cc:CustomBtn}">
...
<Setter Property="Template" Value="{StaticResource XControlTemplate.CustomButton}"/>
<ControlTemplate x:Key="XControlTemplate.CustomButton" TargetType="{x:Type cc:CustomBtn}">
...
Some of the properties defined are used in the templates, some in the styles proper.
The issue we've been facing is that those controls will not display in the designer (most of the time), instead showing as if they're broken. With the error being "Target type does not match type of element"
There are no issues at run time
There are no private/none default constructors in any of our custom controls.
I've got an updated DesignTimeResources
There's a strange discrepancy I've noticed in the error, between the numbers following the control name
There are a bunch of similar questions out there, but the answers attached did not work/apply for me.
Although sometimes after tinkering with the styles/classes the designer will display them in a very basic form.. until it doesn't again.
Styles, classes (apologies for the wall of code):
public class ButtonIcon : Button
{
// VisualTheme determines color theme
public EVisualTheme VisualTheme
{
get { return (EVisualTheme)GetValue(VisualThemeProperty); }
set { SetValue(VisualThemeProperty, value); }
}
// Using a DependencyProperty as the backing store for VisualTheme. This enables animation, styling, binding, etc...
public static readonly DependencyProperty VisualThemeProperty =
DependencyProperty.Register("VisualTheme", typeof(EVisualTheme), typeof(ButtonIcon), new PropertyMetadata(EVisualTheme.Common));
// Icon for Button
public Geometry IconGeometry
{
get { return (Geometry)GetValue(IconGeometryProperty); }
set { SetValue(IconGeometryProperty, value); }
}
// Using a DependencyProperty as the backing store for IconGeometry. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IconGeometryProperty =
DependencyProperty.Register("IconGeometry", typeof(Geometry), typeof(ButtonIcon), new PropertyMetadata(new PathGeometry()));
public double IconSize
{
get { return (double)GetValue(IconSizeProperty); }
set { SetValue(IconSizeProperty, value); }
}
// Using a DependencyProperty as the backing store for IconSize. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IconSizeProperty =
DependencyProperty.Register("IconSize", typeof(double), typeof(ButtonIcon), new PropertyMetadata(40.0));
// Text to be set on Button
public object Caption
{
get { return (object)GetValue(CaptionProperty); }
set { SetValue(CaptionProperty, value); }
}
// Using a DependencyProperty as the backing store for Caption. This enables animation, styling, binding, etc...
public static readonly DependencyProperty CaptionProperty =
DependencyProperty.Register("Caption", typeof(object), typeof(ButtonIcon), new PropertyMetadata(null));
// For running Animations from 0 to 1 in order to change Animation property during Runtime (see examples of usage in code)
public double GradientPercentage
{
get { return (double)GetValue(GradientPercentageProperty); }
set { SetValue(GradientPercentageProperty, value); }
}
// Using a DependencyProperty as the backing store for GradientPercentage. This enables animation, styling, binding, etc...
public static readonly DependencyProperty GradientPercentageProperty =
DependencyProperty.Register("GradientPercentage", typeof(double), typeof(ButtonIcon), new PropertyMetadata(0.0));
public Point IconRenderTransformOrigin
{
get { return (Point)GetValue(IconRenderTransformOriginProperty); }
set { SetValue(IconRenderTransformOriginProperty, value); }
}
// Using a DependencyProperty as the backing store for IconRenderTransformOrigin. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IconRenderTransformOriginProperty =
DependencyProperty.Register("IconRenderTransformOrigin", typeof(Point), typeof(ButtonIcon), new PropertyMetadata(new Point(0.5, 0.5)));
// Angle at which to rotate Icon
public double IconRotationAngle
{
get { return (double)GetValue(IconRotationAngleProperty); }
set { SetValue(IconRotationAngleProperty, value); }
}
// Using a DependencyProperty as the backing store for IconRotationAngle. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IconRotationAngleProperty =
DependencyProperty.Register("IconRotationAngle", typeof(double), typeof(ButtonIcon), new PropertyMetadata(0.0));
// CenterX for Icon rotation
public double IconRotationCenterX
{
get { return (double)GetValue(IconRotationCenterXProperty); }
set { SetValue(IconRotationCenterXProperty, value); }
}
// Using a DependencyProperty as the backing store for IconRotationCenterX. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IconRotationCenterXProperty =
DependencyProperty.Register("IconRotationCenterX", typeof(double), typeof(ButtonIcon), new PropertyMetadata(0.0));
// CenterY for Icon rotation
public double IconRotationCenterY
{
get { return (double)GetValue(IconRotationCenterYProperty); }
set { SetValue(IconRotationCenterYProperty, value); }
}
// Using a DependencyProperty as the backing store for IconRotationCenterY. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IconRotationCenterYProperty =
DependencyProperty.Register("IconRotationCenterY", typeof(double), typeof(ButtonIcon), new PropertyMetadata(0.0));
// ScaleX of Icon
public double IconScaleX
{
get { return (double)GetValue(IconScaleXProperty); }
set { SetValue(IconScaleXProperty, value); }
}
// Using a DependencyProperty as the backing store for IconScaleX. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IconScaleXProperty =
DependencyProperty.Register("IconScaleX", typeof(double), typeof(ButtonIcon), new PropertyMetadata(1.0));
// ScaleY of Icon
public double IconScaleY
{
get { return (double)GetValue(IconScaleYProperty); }
set { SetValue(IconScaleYProperty, value); }
}
// Using a DependencyProperty as the backing store for IconScaleY. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IconScaleYProperty =
DependencyProperty.Register("IconScaleY", typeof(double), typeof(ButtonIcon), new PropertyMetadata(1.0));
// CenterX for Icon scale
public double IconScaleCenterX
{
get { return (double)GetValue(IconScaleCenterXProperty); }
set { SetValue(IconScaleCenterXProperty, value); }
}
// Using a DependencyProperty as the backing store for IconScaleCenterX. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IconScaleCenterXProperty =
DependencyProperty.Register("IconScaleCenterX", typeof(double), typeof(ButtonIcon), new PropertyMetadata(0.0));
// CenterY for Icon scale
public double IconScaleCenterY
{
get { return (double)GetValue(IconScaleCenterYProperty); }
set { SetValue(IconScaleCenterYProperty, value); }
}
// Using a DependencyProperty as the backing store for IconScaleCenterY. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IconScaleCenterYProperty =
DependencyProperty.Register("IconScaleCenterY", typeof(double), typeof(ButtonIcon), new PropertyMetadata(0.0));
public double IconTranslateX
{
get { return (double)GetValue(IconTranslateXProperty); }
set { SetValue(IconTranslateXProperty, value); }
}
// Using a DependencyProperty as the backing store for IconTranslateX. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IconTranslateXProperty =
DependencyProperty.Register("IconTranslateX", typeof(double), typeof(ButtonIcon), new PropertyMetadata(0.0));
public double IconTranslateY
{
get { return (double)GetValue(IconTranslateYProperty); }
set { SetValue(IconTranslateYProperty, value); }
}
// Using a DependencyProperty as the backing store for IconTranslateY. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IconTranslateYProperty =
DependencyProperty.Register("IconTranslateY", typeof(double), typeof(ButtonIcon), new PropertyMetadata(0.0));
}
<Style x:Key="XStyle.ButtonIcon.Action" TargetType="{x:Type cc:ButtonIcon}">
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="Height" Value="{StaticResource XDouble.Height.Button.Default}"/>
<Setter Property="Template" Value="{StaticResource XControlTemplate.ButtonIcon.Action}"/>
<Setter Property="IconSize" Value="30"/>
<Setter Property="GradientPercentage" Value="0.0"/>
<Setter Property="IconGeometry" Value="{x:Null}"/>
<Setter Property="FontSize" Value="{StaticResource XDouble.FontSize.Header3}"/>
<Setter Property="Foreground" Value="{Binding VisualTheme, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type cc:ButtonIcon}}, Converter={rconv:VisualThemeToForegroundConverter}}"/>
<Setter Property="BorderBrush" Value="{StaticResource XBrush.Frame}"/>
<Setter Property="BorderThickness" Value="0"/>
<Style.Triggers>
<Trigger Property="VisualTheme" Value="ListItem">
<Setter Property="BorderThickness" Value="{StaticResource XThickness.Border.Frame}"/>
</Trigger>
<Trigger Property="VisualTheme" Value="DarkOnLight">
<Setter Property="BorderThickness" Value="{StaticResource XThickness.Border.Frame}"/>
</Trigger>
</Style.Triggers>
</Style>
<ControlTemplate x:Key="XControlTemplate.ButtonIcon.Action" TargetType="{x:Type cc:ButtonIcon}">
<Border
x:Name="OuterBorder"
Width="{TemplateBinding Width}"
Height="{TemplateBinding Height}"
BorderBrush="{TemplateBinding BorderBrush}"
Padding="10,0"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{StaticResource XCornerRadius.Frame}">
<Border.Background>
<MultiBinding Converter="{rconv:PercentageToBrushConverter}">
<Binding Path="GradientPercentage" RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type cc:ButtonIcon}}" />
<Binding Path="VisualTheme" RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type cc:ButtonIcon}}" />
</MultiBinding>
</Border.Background>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition x:Name="IconColumn" Width="Auto" />
<ColumnDefinition x:Name="SpacerColumn" Width="10" />
<ColumnDefinition x:Name="CaptionColumn" Width="*" />
</Grid.ColumnDefinitions>
<Border
x:Name="IconBorder"
Width="{TemplateBinding IconSize}"
Height="{TemplateBinding IconSize}"
HorizontalAlignment="Center"
VerticalAlignment="Center">
<Path
x:Name="IconPart"
Data="{TemplateBinding IconGeometry}"
Fill="{TemplateBinding Foreground}"
RenderTransformOrigin="{Binding Path=IconRenderTransformOrigin, RelativeSource={RelativeSource TemplatedParent}}"
Stretch="Uniform">
<Path.RenderTransform>
<TransformGroup>
<ScaleTransform
CenterX="{Binding Path=IconScaleCenterX, RelativeSource={RelativeSource TemplatedParent}}"
CenterY="{Binding Path=IconScaleCenterY, RelativeSource={RelativeSource TemplatedParent}}"
ScaleX="{Binding Path=IconScaleX, RelativeSource={RelativeSource TemplatedParent}}"
ScaleY="{Binding Path=IconScaleY, RelativeSource={RelativeSource TemplatedParent}}" />
<RotateTransform
Angle="{Binding Path=IconRotationAngle, RelativeSource={RelativeSource TemplatedParent}}"
CenterX="{Binding Path=IconRotationCenterX, RelativeSource={RelativeSource TemplatedParent}}"
CenterY="{Binding Path=IconRotationCenterY, RelativeSource={RelativeSource TemplatedParent}}" />
</TransformGroup>
</Path.RenderTransform>
</Path>
</Border>
<ContentPresenter
x:Name="ContentPresenter"
Grid.Column="2"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Content="{TemplateBinding Caption}"
Focusable="False"
SnapsToDevicePixels="True"
TextBlock.FontSize="{TemplateBinding FontSize}"
TextBlock.Foreground="{TemplateBinding Foreground}"/>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Opacity" Value="{StaticResource XDouble.Opacity.Disabled}" />
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
<Trigger.EnterActions>
<BeginStoryboard x:Name="StoryBoardMouseEnter" Storyboard="{StaticResource XStoryboard.DoubleAnimation.GradientPercentage.RunUp}" />
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard x:Name="StoryBoardMouseLeave" Storyboard="{StaticResource XStoryboard.DoubleAnimation.GradientPercentage.RunDown}" />
</Trigger.ExitActions>
</Trigger>
<Trigger Property="Caption" Value="{x:Null}">
<Setter TargetName="ContentPresenter" Property="Visibility" Value="Collapsed" />
<Setter TargetName="IconColumn" Property="Width" Value="*" />
<Setter TargetName="SpacerColumn" Property="Width" Value="0" />
<Setter TargetName="CaptionColumn" Property="Width" Value="0" />
</Trigger>
<Trigger Property="Caption" Value="">
<Setter TargetName="ContentPresenter" Property="Visibility" Value="Collapsed" />
<Setter TargetName="IconColumn" Property="Width" Value="*" />
<Setter TargetName="SpacerColumn" Property="Width" Value="0" />
<Setter TargetName="CaptionColumn" Property="Width" Value="0" />
</Trigger>
<Trigger Property="IconGeometry" Value="{x:Null}">
<Setter TargetName="IconPart" Property="Visibility" Value="Collapsed"/>
<Setter TargetName="SpacerColumn" Property="Width" Value="0"/>
<Setter TargetName="IconColumn" Property="Width" Value="0"/>
<Setter TargetName="ContentPresenter" Property="Grid.Column" Value="0"/>
<Setter TargetName="ContentPresenter" Property="Grid.ColumnSpan" Value="3"/>
<Setter TargetName="ContentPresenter" Property="HorizontalAlignment" Value="Center"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
I have a SearchTextBox custom control, pictured here:
When the mouse clicks on it, that label disappears. If the user clicks away and there is no text, the label reappears (if the user clicks away but leaves text there, the text stays and the label stays hidden). This button replaces the image when the user starts typing:
When the user clicks the button, the text is cleared.
That's all working correctly. The odd behavior I'm talking about is that when the clear button is clicked, the label flashes on the control for a split-second before disappearing (it should remain hidden the entire time). Because the Multitrigger in the XAML uses IsFocused, I thought maybe I could fix the problem simply by calling Focus() in the code-behind before the Click event finishes. This seems a little hacky, but it actually worked. My question is, why do I have to do that to make it work "right?"
Snippet from Generic.xaml:
<SolidColorBrush x:Key="TextBoxBorder" Color="#ababab"/>
<Style TargetType="{x:Type ui:SearchTextBox}">
<Setter Property="AllowDrop" Value="True" />
<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="FocusVisualStyle" Value="{x:Null}" />
<Setter Property="LabelText" Value="Search for..." />
<Setter Property="LabelTextColor" Value="Gray" />
<Setter Property="Padding" Value="1" />
<Setter Property="ScrollViewer.PanningMode" Value="VerticalFirst" />
<Setter Property="Stylus.IsFlicksEnabled" Value="False" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ui:SearchTextBox}">
<Border x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="True">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=ActualHeight}" />
</Grid.ColumnDefinitions>
<Label x:Name="LabelText"
Grid.Column="0"
Foreground="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=LabelTextColor}"
Content="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=LabelText}"
Padding="0"
Margin="5,0,0,0"
FontStyle="Italic"
VerticalAlignment="Center"
Visibility="Hidden" />
<ScrollViewer Grid.Column="0" Panel.ZIndex="1" x:Name="PART_ContentHost" Background="{TemplateBinding Background}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="Center" Margin="5,0,0,0" Padding="0"/>
<Image x:Name="Image" Grid.Column="1" Visibility="Hidden" Source="search2.png" Width="15" Height="15" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
<Button x:Name="PART_Button" Grid.Column="1" Width="15" Height="15">
<Border HorizontalAlignment="Center" VerticalAlignment="Center">
<Image Source="searchstop.png" />
</Border>
</Button>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="Text" Value="">
<Setter TargetName="Image" Property="Visibility" Value="Visible" />
<Setter TargetName="PART_Button" Property="Visibility" Value="Hidden" />
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="Text" Value="" />
<Condition Property="IsFocused" Value="False" />
</MultiTrigger.Conditions>
<Setter TargetName="LabelText" Property="Visibility" Value="Visible" />
<Setter TargetName="PART_ContentHost" Property="Visibility" Value="Hidden" />
</MultiTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
SearchTextBox.cs:
public class SearchTextBox : TextBox
{
public static readonly DependencyProperty LabelTextProperty;
public static readonly DependencyProperty LabelTextColorProperty;
public static readonly DependencyProperty HasTextProperty;
private static readonly DependencyPropertyKey HasTextPropertyKey;
public static readonly DependencyProperty SourceProperty;
private static readonly DependencyProperty IsMouseLeftButtonDownProperty;
static SearchTextBox()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(SearchTextBox), new FrameworkPropertyMetadata(typeof(SearchTextBox)));
LabelTextProperty = DependencyProperty.Register("LabelText", typeof(string), typeof(SearchTextBox));
LabelTextColorProperty = DependencyProperty.Register("LabelTextColor", typeof(Brush), typeof(SearchTextBox));
HasTextPropertyKey = DependencyProperty.RegisterReadOnly("HasText", typeof(bool), typeof(SearchTextBox), new PropertyMetadata());
HasTextProperty = HasTextPropertyKey.DependencyProperty;
SourceProperty = DependencyProperty.Register("Source", typeof(ImageSource), typeof(SearchTextBox));
IsMouseLeftButtonDownProperty = DependencyProperty.Register("IsMouseLeftButtonDown", typeof(bool), typeof(SearchTextBox), new PropertyMetadata());
}
protected override void OnTextChanged(TextChangedEventArgs e)
{
base.OnTextChanged(e);
HasText = Text.Length != 0;
}
public string LabelText
{
get { return (string)GetValue(LabelTextProperty); }
set { SetValue(LabelTextProperty, value); }
}
public Brush LabelTextColor
{
get { return (Brush)GetValue(LabelTextColorProperty); }
set { SetValue(LabelTextColorProperty, value); }
}
public ImageSource Source
{
get { return (ImageSource)GetValue(SourceProperty); }
set { SetValue(SourceProperty, value); }
}
public bool HasText
{
get { return (bool)GetValue(HasTextProperty); }
private set { SetValue(HasTextPropertyKey, value); }
}
public bool IsMouseLeftButtonDown
{
get { return (bool)GetValue(IsMouseLeftButtonDownProperty); }
private set { SetValue(IsMouseLeftButtonDownProperty, value); }
}
public override void OnApplyTemplate()
{
Button b = GetTemplateChild("PART_Button") as Button;
if (b != null)
{
b.Click += OnClick;
}
base.OnApplyTemplate();
}
private void OnClick(object sender, RoutedEventArgs e)
{
Text = "";
Focus();
e.Handled = true;
}
}
The flashing most likely occurs because the focus is moving away from your control to the clear button if you press it.
You can fix that be making the clear button non-focusable, i.e.
<Button x:Name="PART_Button" Focusable="False" ... />
Then the focus does not move when you press the button.
I have a very simple Button-based control that displays an ellipse with color taken from custom dependency control called "Brush".
The template displays ellipse with proper color, but the Setters in Trigger do not recognize the "Brush" property (errors highlighted in the XAML file below).
How to access the "Brush" property in the setter so I can change its value on MouseOver?
XAML:
<Button x:Class="WpfTest.EllipseButton"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfTest"
Style="{DynamicResource localStyle}"
Name="ellipseButton">
<Button.Resources>
<Style x:Key="localStyle"
TargetType="local:EllipseButton">
<Style.Setters>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid>
<Ellipse Fill="{Binding ElementName=ellipseButton, Path=Brush}" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style.Setters>
<Style.Triggers>
<Trigger Property="IsMouseOver"
Value="True">
<!-- ERROR HERE: The property "Brush" is not a dependency property. -->
<Setter Property="Brush"
Value="Blue" />
<!-- ERROR HERE: The "BrushProperty" is not recognized or is not accessible. -->
<Setter Property="BrushPropety"
Value="Blue" />
</Trigger>
</Style.Triggers>
</Style>
</Button.Resources>
<Grid>
</Grid>
</Button>
code-behind:
public partial class EllipseButton : Button
{
public static readonly DependencyProperty BrushProperty = DependencyProperty.Register(
"Fill",
typeof(Brush),
typeof(EllipseButton),
new FrameworkPropertyMetadata(new SolidColorBrush(Colors.Gray)));
public Brush Brush
{
get
{
return (Brush)GetValue(BrushProperty);
}
set
{
SetValue(BrushProperty, value);
}
}
public EllipseButton()
{
InitializeComponent();
}
}
Your property is called "Fill" not "Brush":
public static readonly DependencyProperty BrushProperty = DependencyProperty.Register(
"Fill", //Error is here
typeof(Brush),
typeof(EllipseButton),
new FrameworkPropertyMetadata(new SolidColorBrush(Colors.Gray)));
Change that to:
public static readonly DependencyProperty BrushProperty = DependencyProperty.Register(
"Brush",
typeof(Brush),
typeof(EllipseButton),
new FrameworkPropertyMetadata(new SolidColorBrush(Colors.Gray)));
Okay, several things needed to be done:
1) Rename dependency property name to "Brush" (it was wrongly named "Fill") - thanks to HighCore
2) When the control is used in code, remove setting the "Brush" property - the local value overriden the setters from style.
3) Move the style from custom control to higher level (e.g. under "Themes\Generic.xaml")
4) Remove x:Key attribute from the style and keep just the type name (still don't know why...)
5) Add default value of the "Brush" property to the style setter (again, not sure why...)
Fixed EllipseButton.xaml:
<Button x:Class="WpfTest.EllipseButton" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<Grid/>
</Button>
fixed code-behind:
public partial class EllipseButton
{
public static readonly DependencyProperty BrushProperty = DependencyProperty.Register(
"Brush",
typeof(Brush),
typeof(EllipseButton),
new FrameworkPropertyMetadata(null));
public Brush Brush
{
get
{
return (Brush)GetValue(BrushProperty);
}
set
{
SetValue(BrushProperty, value);
}
}
public EllipseButton()
{
InitializeComponent();
}
}
fixed style (Generic.xaml):
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfTest">
<Style TargetType="local:EllipseButton">
<Style.Setters>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid>
<Ellipse Fill="{Binding ElementName=ellipseButton, Path=Brush}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="Brush" Value="Pink"/>
</Style.Setters>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Brush" Value="Red"/>
</Trigger>
</Style.Triggers>
</Style>
</ResourceDictionary>
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>
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.