WPF Button with hot image - wpf

<Button Margin="10,0,2,0" Width="90" Command="Open">
<Button.Style>
<Style TargetType="Button">
<Setter Property="Content" Value="{Binding Source={StaticResource tstImage}}"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Content" Value="{Binding Source={StaticResource tstImage1}}"/>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Content" Value="{Binding Source={StaticResource tstImage2}}"/>
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
I would like to create a button support hot image: When the mouse hover on the button, the image changes to another one.
I implement a button instance with above code. But actually I would like to create a template that every button can use this style. It is cumbersome to write the same style for every button.
I think it should like:
<Style TargetType="Button">
<Setter Property="Content" Value="{Binding Source= normalImage"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Content" Value="{Binding Source=hotImage"/>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Content" Value="{Binding Source=disableImage"/>
</Trigger>
</Style.Triggers>
</Style>
And then use the style like:
<Button command="Open" normalImage="icon1.png" hotImage="icon2.png" disableImage="icon3.png"/>
I am a fresher to WPF, I have tried many ways to do that but still stuck in this problem.
I would like to create a template or style that can apply to every button. The second and third snippet is what I want to achieve. But it not works. In the second snippet, the normalImage,hotImage and disableImage are dynamic paths that can be specified by buttons just as what I wrote in the third snippet.
Please help. Thanks.
I am trying to use attached-property, but I met another problem:
Style binding attached-property didn't show up anything

I solve it myself by using attached property. It may help others, so I post out my code.
Firstly, create the attached property:
class ImageAttached : DependencyObject
{
public static Image GetNormalImage(DependencyObject obj)
{
return (Image)obj.GetValue(NormalImageProperty);
}
public static void SetNormalImage(DependencyObject obj, Image value)
{
obj.SetValue(NormalImageProperty, value);
}
// Using a DependencyProperty as the backing store for NormalImage. This enables animation, styling, binding, etc...
public static readonly DependencyProperty NormalImageProperty =
DependencyProperty.RegisterAttached("NormalImage", typeof(Image), typeof(ImageAttached), new PropertyMetadata((Image)null));
public static Image GetHotImage(DependencyObject obj)
{
return (Image)obj.GetValue(HotImageProperty);
}
public static void SetHotImage(DependencyObject obj, Image value)
{
obj.SetValue(HotImageProperty, value);
}
// Using a DependencyProperty as the backing store for HotImage. This enables animation, styling, binding, etc...
public static readonly DependencyProperty HotImageProperty =
DependencyProperty.RegisterAttached("HotImage", typeof(Image), typeof(ImageAttached), new PropertyMetadata((Image)null));
public static Image GetDisableImage(DependencyObject obj)
{
return (Image)obj.GetValue(DisableImageProperty);
}
public static void SetDisableImage(DependencyObject obj, Image value)
{
obj.SetValue(DisableImageProperty, value);
}
// Using a DependencyProperty as the backing store for DisableImage. This enables animation, styling, binding, etc...
public static readonly DependencyProperty DisableImageProperty =
DependencyProperty.RegisterAttached("DisableImage", typeof(Image), typeof(ImageAttached), new PropertyMetadata((Image)null));
}
Secondly, create a style target to a button:
<Style x:Key="btnStyle" TargetType="{x:Type Button}">
<Setter Property="Content" Value="{Binding Path=(local:ImageAttached.NormalImage), RelativeSource={RelativeSource Self}}"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Content" Value="{Binding Path=(local:ImageAttached.HotImage), RelativeSource={RelativeSource Self}}"/>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Content" Value="{Binding Path=(local:ImageAttached.DisableImage), RelativeSource={RelativeSource Self}}"/>
</Trigger>
</Style.Triggers>
</Style>
Lastly, use it. e.g:
<Button x:Name="btn1" Click="Button_Click_1" Width="100" Height="48" Style="{DynamicResource btnStyle}"
local:ImageAttached.NormalImage="{StaticResource tstImage}"
local:ImageAttached.HotImage="{StaticResource tstImage2}"
local:ImageAttached.DisableImage="{StaticResource tstImage1}"/>

Related

How can I fall back to a high contrast color in WPF?

I have some XAML which sets the foreground color directly:
<Style x:Key="HomeHeaderText" TargetType="TextBlock">
<Setter Property="FontSize" Value="24" />
<Setter Property="FontFamily" Value="Segoe Light UI" />
<Setter Property="Foreground" Value="#FF606060" />
<Setter Property="Margin" Value="0,50,0,30" />
</Style>
I would like to detect in the style whether or not the system is in high contrast mode, and fall back to one of the system colors if so.
How can one do this using styles?
I tried setting this using a trigger, but this results in XamlParseException at runtime:
<Style x:Key="HomeHeaderText" TargetType="TextBlock">
<Setter Property="FontSize" Value="24" />
<Setter Property="FontFamily" Value="Segoe Light UI" />
<Setter Property="Foreground" Value="#FF606060" />
<Setter Property="Margin" Value="0,50,0,30" />
<Style.Triggers>
<DataTrigger Binding="{x:Static SystemParameters.HighContrast}" Value="True">
<Setter Property="Foreground"
Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" />
</DataTrigger>
</Style.Triggers>
</Style>
The problem with your attempt is that DataTrigger.Binding wants a binding, but you gave it a direct value. You can solve this by setting the binding source:
{Binding Source={x:Static SystemParameters.HighContrast}}
However, this will not be dynamic -- the style would not update if someone toggles high-contrast while the application is running. Ideally it would be nice to have something like this:
{Binding Source={x:Static SystemParameters}, Path=HighContrast}
But unfortunately that isn't possible since it's a static property. So, binding to the HighContrastKey resource is the better option. Instead of using Tag, you could bind this to an attached property. Come to think of it, Microsoft probably should have implemented SystemParameters as attached properties in the first place. Try something like this:
public static class SystemParameterProperties {
public static readonly DependencyProperty HighContrastProperty =
DependencyProperty.RegisterAttached("HighContrast", typeof(bool), typeof(SystemParameterProperties),
new FrameworkPropertyMetadata() {Inherits = true});
public static bool GetHighContrast(DependencyObject obj) {
return (bool)obj.GetValue(HighContrastProperty);
}
public static void SetHighContrast(DependencyObject obj, bool value) {
obj.SetValue(HighContrastProperty, value);
}
}
I used Inherits = true on the property so that we can just set it on the outermost container and let it be accessible everywhere, i.e.:
<Window ...
xmlns:attachedProperties="..."
attachedProperties:SystemParameterProperties.HighContrast="{DynamicResource ResourceKey={x:Static Member=SystemParameters.HighContrastKey}}">
...
</Window>
Finally, your trigger would be:
<Trigger Property="attachedProperties:SystemParameterProperties.HighContrast" Value="True">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" />
</Trigger>
I'd have solved leveraging an helper:
public class HighContrastHelper
: DependencyObject
{
#region Singleton pattern
private HighContrastHelper()
{
SystemParameters.StaticPropertyChanged += SystemParameters_StaticPropertyChanged;
}
private static HighContrastHelper _instance;
public static HighContrastHelper Instance
{
get
{
if (_instance == null)
_instance = new HighContrastHelper();
return _instance;
}
}
#endregion
void SystemParameters_StaticPropertyChanged(object sender, PropertyChangedEventArgs e)
{
Console.WriteLine(e.PropertyName);
if (e.PropertyName == "HighContrast")
{
HighContrastHelper.Instance.IsHighContrast = SystemParameters.HighContrast;
}
}
#region DP IsHighContrast
public static readonly DependencyProperty IsHighContrastProperty = DependencyProperty.Register(
"IsHighContrast",
typeof(bool),
typeof(HighContrastHelper),
new PropertyMetadata(
false
));
public bool IsHighContrast
{
get { return (bool)GetValue(IsHighContrastProperty); }
private set { SetValue(IsHighContrastProperty, value); }
}
#endregion
}
Afterward, the usage in your code is straightforward:
<Style x:Key="HomeHeaderText" TargetType="TextBlock">
<Setter Property="FontSize" Value="24" />
<Setter Property="FontFamily" Value="Segoe Light UI" />
<Setter Property="Foreground" Value="#FF606060" />
<Setter Property="Margin" Value="0,50,0,30" />
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsHighContrast, Source={x:Static local:HighContrastHelper.Instance}}" Value="True">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" />
</DataTrigger>
</Style.Triggers>
</Style>
Hope it helps.
You can bind it to Tag property of textblock and use that in DataTrigger like below:
<Style x:Key="MyTextBoxStyle" TargetType="{x:Type TextBlock}">
<Setter Property="Tag" Value="{DynamicResource {x:Static SystemParameters.HighContrastKey}}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Tag , RelativeSource= {x:Static RelativeSource.Self}}" Value="True">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" />
</DataTrigger>
</Style.Triggers>
</Style>

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"/>

Function call within XAML code?

I'd like to set a style on all my TextBox controls that does the following when it receives keyboard focus:
1) Change the background color
2) Call .SelectAll() to highlight all text
I have this so far:
<Style TargetType="TextBox">
<Style.Triggers>
<Trigger Property="IsKeyboardFocusWithin" Value="True">
<Setter Property="Background">
<Setter.Value>
<SolidColorBrush Color="#FFFFD1D9"/>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
Is there a way to also call .SelectAll() ? Thanks.
You can do this using attached behaviours.
Example
public static class TextBoxBehaviour
{
public static bool GetSelectAll(TextBoxBase target)
{
return (bool)target.GetValue(SelectAllAttachedProperty);
}
public static void SetSelectAll(TextBoxBase target, bool value)
{
target.SetValue(SelectAllAttachedProperty, value);
}
public static readonly DependencyProperty SelectAllAttachedProperty = DependencyProperty.RegisterAttached("SelectAll", typeof(bool), typeof(TextBoxBehaviour), new UIPropertyMetadata(false, OnSelectAllAttachedPropertyChanged));
static void OnSelectAllAttachedPropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
((TextBoxBase)o).SelectAll();
}
}
Usage
<Style TargetType="{x:Type TextBox}" xmlns:behaviours="clr-namespace:Controls.Behaviours">
<Style.Triggers>
<Trigger Property="IsKeyboardFocusWithin" Value="True">
<Setter Property="Background" Value="#FFFFD1D9"/>
<Setter Property="behaviours:TextBoxBehaviour.SelectAll" Value="True"/>
</Trigger>
</Style.Triggers>
</Style>
References
Josh Smith on Attached Behaviours
NB: Wasn't able to test the above implementation, in theory though it should just work™.
HTH,

Using custom dependency properties as DataTrigger in WPF

I have a custom dependency property that I would like to use as a data trigger. Here is the code behind:
public static readonly DependencyProperty BioinsulatorScannedProperty =
DependencyProperty.Register(
"BioinsulatorScanned",
typeof(bool),
typeof(DisposablesDisplay),
new FrameworkPropertyMetadata(false));
public bool BioinsulatorScanned
{
get
{
return (bool)GetValue(BioinsulatorScannedProperty);
}
set
{
SetValue(BioinsulatorScannedProperty, value);
}
}
I have created a style and control template. My goal is to change the color of some text when the dependency prop is set to true...
<Style x:Key="TreatEye" TargetType="Label">
<Setter Property="Foreground" Value="#d1d1d1" />
<Setter Property="FontWeight" Value="Bold" />
<Setter Property="FontSize" Value="30" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Label">
<Canvas>
<TextBlock x:Name="bioinsulatorText"
Canvas.Left="21" Canvas.Top="33"
Text="Bioinsulator" />
<TextBlock Canvas.Left="21" Canvas.Top="70"
Text="KXL Kit" />
</Canvas>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding BioinsulatorScanned}"
Value="True">
<Setter TargetName="bioinsulatorText"
Property="Foreground" Value="Black" />
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Despite successfully setting the dependency prop to true programmatically, This trigger condition never fires. This is a real pain to debug!
Thanks in advance.
In this case I am switching the visibility of a button using a datatrigger based on a dependency property FirstLevelProperty.
public static readonly DependencyProperty FirstLevelProperty = DependencyProperty.Register("FirstLevel", typeof(string), typeof(MyWindowClass));
public string FirstLevel
{
get
{
return this.GetValue(FirstLevelProperty).ToString();
}
set
{
this.SetValue(FirstLevelProperty, value);
}
}
You can reference the dependency property FirstLevel(Property) contained (in this case) in a window by using the RelativeSource binding. Also you should set the default setting in the style, that will be overridden by the datatrigger.
<Button.Style>
<Style TargetType="Button">
<Style.Triggers>
<DataTrigger
Binding="{Binding Path=FirstLevel,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type Window}}}"
Value="SomeValue">
<Setter Property="Visibility"
Value="Hidden" />
</DataTrigger>
</Style.Triggers>
<Setter Property="Visibility" Value="Visible" />
</Style>
</Button.Style>
It looks like your dependency property is defined inside a DisposableDisplay object that you created. In order for the binding specified to work, an instance of that DisposableDisplay object must be set as the DataContext of the control (label in this case) or any of its ancestors.

WPF: Is it standard that when a menuitem is disabled the icon is not greyed out?

I have menuitems with icons and when it is disabled the icon remains the same. Is it up to me to supply a disabled icon and, if so, does this also apply to menuitems bound to a command?
Found Jobi's answer helpful. Here's another way to accomplish the same thing using an Image Style and the MenuItem.Icon:
<MenuItem Header="Add ..." Command="{Binding AddCommand}" >
<MenuItem.Icon>
<Image Source="{StaticResource AddImage}" Style="{StaticResource EnableDisableImageStyle}"/>
</MenuItem.Icon>
</MenuItem>
And the Style:
<Style x:Key="EnableDisableImageStyle" TargetType="{x:Type Image}">
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Opacity" Value="0.75"/>
<Setter Property="BitmapEffect">
<Setter.Value>
<BlurBitmapEffect Radius="2.0" KernelType="Gaussian"/>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
Yes it is totally up to you. Because you have provided an Icon file. So you need to create Style.Trigger on MenuItem to give disabled effect on that. Either do a Opacity =0.5 or switch image to a different .ico image while IsEnabled=False in the template
Actually I had the same problem and and I could not find a satisfying answer to this question. Changing the opacity is quite simple, but what if you want to change the complete appearance of an Icon for a MenuItem. It took me two days to find a simple solution. Since I am pretty new to WPF I had to read through different concepts and Attached Properties was one that got me to my solution. Here is my code for changing the Icon depending on enabled/disabled MenuItem:
Here is the code, which defines the Attached Properties used by the style setter:
public static class AltImageProvider
{
public static string GetAltImage(DependencyObject obj)
{
return (string)obj.GetValue(AltImageProperty);
}
public static void SetAltImage(DependencyObject obj, string value)
{
obj.SetValue(AltImageProperty, value);
}
public static readonly DependencyProperty AltImageProperty =
DependencyProperty.RegisterAttached("AltImage",typeof(string),typeof(AltImageProvider));
public static string GetDefImage(DependencyObject obj)
{
return (string)obj.GetValue(DefImageProperty);
}
public static void SetDefImage(DependencyObject obj, string value)
{
obj.SetValue(DefImageProperty, value);
}
public static readonly DependencyProperty DefImageProperty =
DependencyProperty.RegisterAttached("DefImage",typeof(string),typeof(AltImageProvider));
}
Here is the code defining a style for Images like the Icons, that uses the Attached Properties (DefImage and AltImage):
<Style x:Key="ImageDisableAltImageStyle" TargetType="Image">
<Setter Property="Image.Source" Value="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=(local:AltImageProvider.DefImage)}"/>
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Image.Source" Value="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=(local:AltImageProvider.AltImage)}"/>
</Trigger>
</Style.Triggers>
</Style>
Here is how to set the style and Icons for a MenuItem:
<MenuItem.Icon>
<Image Style="{StaticResource ImageDisableAltImageStyle}" local:AltImageProvider.DefImage="/Icon/play_green.ico" local:AltImageProvider.AltImage="/Icon/play_grey.ico"/>
</MenuItem.Icon>
This seems odd, but all I did was create the following style. You'd think this is the built in behavior, but without this (like the OP), the image isn't dimmed. With this, the image is affected, too. And I don't need to modify anything else in my app.
<Style TargetType="MenuItem">
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Opacity" Value="0.5"/>
</Trigger>
</Style.Triggers>
</Style>
This targets only the icons on all MenuItems, without needing any keys and without affecting the look of the menu text.
<Style TargetType="MenuItem">
<Style.Resources>
<!-- Replace Frame with the actual icon container, e.g. Image -->
<Style TargetType="Frame">
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Opacity" Value="0.5"/>
</Trigger>
</Style.Triggers>
</Style>
</Style.Resources>
</Style>

Resources