'Graying Out' a WPF button image? - wpf

I have a simple Button control that contains an Image object as its content. I want so set the Image opacity to 0.5 when the Button is disabled to provide an additional visual cue as to the Button status.
What is the simplest way to accomplish that result in XAML? Thanks for your help.

Use a trigger in the Image style. (It would be more natural to put it in the Button style, but the Button style can't easily affect the Image for annoying technical reasons. It could be done in the Button's ControlTemplate but that's overkill for what you want here.)
<Button>
<Image Source="something.png">
<Image.Style>
<Style TargetType="Image">
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Opacity" Value="0.5" />
</Trigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
</Button>
Note we are taking advantage here of the fact that the Image will be disabled when the Button is disabled, so we can trigger directly on the Image's own IsEnabled property. In other cases, the Button property we want to trigger on might not be inherited by the Image; in that case, we'd need to use a DataTrigger with the FindAncestor RelativeSource to bind to the containing button.

If you want something more generic, put this in your resources section for your window or UserControl .
<UserControl.Resources>
<Style x:Key="ImageEnabled" TargetType="Image">
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Opacity" Value="0.25"></Setter>
</Trigger>
</Style.Triggers>
</Style>
</UserControl.Resources>
And in the actual button just do this
<Button>
<Image Source="{StaticResource LinkImage}" Style="{StaticResource ImageEnabled}"/>
</Button>

Here's a more generic style you can apply:
<Style TargetType="Button">
<Style.Resources>
<Style TargetType="Image">
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Opacity" Value="0.5" />
</Trigger>
</Style.Triggers>
</Style>
</Style.Resources>
</Style>

If you want the entire button greyed out, styling the button itself has worked for me. It seems to grey out all button content.
<Button.Style>
<Style TargetType="Button">
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Opacity" Value=".75"/>
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>

previous solutions only change opacity and not the color of the image, to gray out an image one need to convert the image to grayscale as follow. I did it for a toggleButton but the same method can be used on a button.
<ToggleButton>
<ToggleButton.Style>
<Style TargetType="{x:Type ToggleButton}">
<Setter Property="Content">
<Setter.Value>
<Image
RenderOptions.BitmapScalingMode="HighQuality"
RenderOptions.EdgeMode="Aliased"
Source="/Resources/imageForButton.png"
Stretch="UniformToFill" />
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsChecked" Value="False">
<Setter Property="Content">
<Setter.Value>
<Image
RenderOptions.BitmapScalingMode="HighQuality"
RenderOptions.EdgeMode="Aliased"
Stretch="UniformToFill">
<Image.Source>
<FormatConvertedBitmap
x:Name="myImageFormat"
DestinationFormat="Gray8"
Source="/Resources/imageForButton.png" />
</Image.Source>
</Image>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</ToggleButton.Style>
</ToggleButton>

Related

how to use Style.Triggers for Canvas in WPF

I want to make a simple effect for a canvas by this code, but it doesn't work.
<Canvas.Style>
<Style>
<Setter Property="Canvas.Background" Value="Transparent"/>
<Style.Triggers>
<Trigger Property="Canvas.IsMouseOver" Value="True">
<Setter Property="Canvas.Background" Value="LightSkyBlue">
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</Canvas.Style>
Your <Style> element is missing a TargetType. Because controls can be made up of multiple controls you can use TargetType to target different control types and doing so will also give you contextual intellisense for the children Elements.
<Canvas HorizontalAlignment="Left" Height="100" Margin="215,128,0,0" VerticalAlignment="Top" Width="100">
<Canvas.Style>
<Style TargetType="Canvas">
<Setter Property="Background" Value="Transparent"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Canvas.Background" Value="LightSkyBlue">
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</Canvas.Style>
</Canvas>
I feel like CSS really did a great job at making styling easy so it's a bummer that WPF has to be so esoteric.

Change style of a child control on MouseOver in WPF

I have a custom control that holds two Rectangles and several TextBoxes. I wish to change the background color of the Rectangle on MouseOver.
I add trigger as following:
<Rectangle
Grid.Column="1"
Fill="#FF383838"
Grid.ColumnSpan="3"
Margin="0,4,4,4">
<Rectangle.Style>
<Style TargetType="{x:Type Rectangle}">
<Setter Property="Fill" Value="#FF383838" />
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Fill" Value="#FF575757" />
</Trigger>
</Style.Triggers>
</Style>
</Rectangle.Style>
</Rectangle>
But since rectangle is part of my control, I assume the event is not firing.
Setting a property via XAML will be applied over the style properties that you try to set. To fix this, remove Fill=#FF383838 so you should have:
<Rectangle Grid.Column="1"
Grid.ColumnSpan="3"
Margin="0,4,4,4">
//... rest of code here
Try this code:
<Window.Resources>
<Style TargetType="Rectangle" x:Key="test">
<Setter Property="Fill" Value="#FF383838" />
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Fill" Value="#FF575757" />
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Rectangle Style="{StaticResource test}" />

why this style is not working in wpf

I have a style for button as follow:
<Style TargetType="Button" x:Key="BlackButton">
<Setter Property="Background" Value="Black"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="red" />
</Trigger>
</ControlTemplate.Triggers>
<ContentPresenter />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
and a button on which is defined as follow:
<Button Canvas.Left="19" Canvas.Top="520" Height="34" Width="107"
Style="{StaticResource BlackButton}" />
But when I run application, I can not see the button. Its background set to none.
If I change the style as follow:
<Style TargetType="Button" x:Key="BlackButton">
<Setter Property="Background" Value="Black"/>
</Style>
(Removing the template) then the button is shown but its background is not changing.
What is wrong with this xaml code?
You've overridden the template of your control in order to set the MouseOver trigger. That means your control template is otherwise empty - and so nothing is drawn for your button.
You can fix that by moving your triggers to the style itself, like this:
<Style TargetType="Button" x:Key="BlackButton">
<Setter Property="Background" Value="Black"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="red" />
</Trigger>
</Style.Triggers>
</Style>
However, what you're likely to run into now is that the button's built-in MouseOver animation will override your red background. You'll see a flash of red, followed by a transition to the default Windows colour. One way to fix that thoroughly is to take a full copy of the default Button template (using Expression Blend is the easiest way to do this) and remove the animations from it.
Well your ControlTemplate is simply empty, although you have a ControlPresenter in it. But since its Content property is not set, it's also empty. To have a Background you will have to add a Border.
<Style TargetType="Button" x:Key="BlackButton">
<Setter Property="Background" Value="Black"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="red" />
</Trigger>
</ControlTemplate.Triggers>
<Border Background="{TemplateBinding Background}">
<ContentPresenter Content="{TemplateBinding Content}"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
This should show you something.

WPF: the style of control does not work unless the control template created for style

It almost gets me mad in recent days. I have a textbox and the style in xaml file.
But the style without a control template cannot take effect on textbox. Whereas, a control template works, but control template seems to overwrite the textbox totally, the default behaviors loses of textbox such as editing, inputing or selecting...
Here is content of xaml with the control template:
<Style TargetType="{x:Type TextBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border Name="tbBorder" Background="White" BorderThickness="0.6" BorderBrush="#B9B9B9">
<ContentPresenter/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsFocused" Value="true">
<Setter Property="BorderBrush" Value="#4D90FE" />
</Trigger>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="BorderBrush" Value="#4D90FE" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
And here is the simple style which does not work at all,
<Style TargetType="{x:Type TextBox}">
<Style.Triggers>
<Trigger Property="IsFocused" Value="true">
<Setter Property="BorderBrush" Value="#4D90FE" />
</Trigger>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="BorderBrush" Value="#4D90FE" />
</Trigger>
</Style.Triggers>
</Style>
Thanks!
update: the entire textbox's code snipt:
<TextBox Height="23" HorizontalAlignment="Left" Margin="114,53,0,0" Name="textBox1" VerticalAlignment="Top" Width="150" Text="{Binding Path=TraderAccount, Mode=OneWayToSource, NotifyOnValidationError=True}" BorderBrush="#FFB9B9B9" BorderThickness="1" >
<TextBox.Style>
<Style TargetType="{x:Type TextBox}">
<Style.Triggers>
<Trigger Property="IsFocused" Value="true">
<Setter Property="BorderBrush" Value="Red" />
</Trigger>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="BorderBrush" Value="Red" />
</Trigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
The style setter is working for me but the issue I see is that the controls animations are animating away the style that's just been set.
You may want to extract the original control template and redefine that rather than completely redefining it. As far as I know The textbox control is more complex than just a border with a content presenter (I've never extracted the control template for it though!) and its likely to have a couple of borders that work to give it all it's states etc
You can use Blend to do this - in the absence of Blend there is the MSDN resource for control templates and styles:
http://msdn.microsoft.com/en-us/library/aa970773.aspx
Edit:
For starters it looks to me like you are missing the content 'PART' in your redefined template
<ScrollViewer Margin="0" x:Name="PART_ContentHost" />
Edit 2:
You are saying it doesn't work... this works for me on WPF using .NET Framework 4.0 - I changed the border colour to 'Red' instead to make sure I could see the effect and it definitely works, aside from the red fading immediately because the controls visual state is changed by the Visual State Manager (which is why you need to edit the control template and change the visual states)
<TextBox>
<TextBox.Style>
<Style TargetType="TextBox">
<Style.Triggers>
<Trigger Property="IsFocused" Value="true">
<Setter Property="BorderBrush" Value="Red" />
</Trigger>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="BorderBrush" Value="Red" />
</Trigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
When you hover over the box, you get a red border which immediately fades
Does this XAML not work for you at all??
You did not post TextBox code but I assume (it happened to me too) that you simply forgot to set BorderThickness of your textbox:
<TextBox BorderThickness="4">
<TextBox.Style>
<Style TargetType="{x:Type TextBox}">
<Style.Triggers>
<Trigger Property="IsFocused" Value="true">
<Setter Property="BorderBrush" Value="#4D90FE" />
</Trigger>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="BorderBrush" Value="#4D90FE" />
</Trigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
Your style does work, set a property like Background in your style without the template and you will see that it does get applied. However, like someone else mentioned, the reason you do not see any changes is because of animation in the default WPF control template for TextBox (Animation values always take precedence over local values, setters and triggers). When you redefine the control template, those animations are no longer there and so your example works. What you could do is take the default TextBox template and modify it to suit your purposes (can be found here: http://msdn.microsoft.com/en-us/library/cc645061%28VS.95%29.aspx).

A problem with using triggers in XAML

At the code below, when a mouse is over the grid, the grid's Background is expected to be red, but it isn't executed as expected.
<Grid>
<Grid.Style>
<Style TargetType="{x:Type Grid}">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="Red"/>
</Trigger>
</Style.Triggers>
</Style>
</Grid.Style>
</Grid>
However, if I added the Setter to make the Background green, it would be executed properly.
<Grid>
<Grid.Style>
<Style TargetType="{x:Type Grid}">
<Setter Property="Background" Value="Green"/><!-- at the former, added code-->
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="Red"/>
</Trigger>
</Style.Triggers>
</Style>
</Grid.Style>
</Grid>
I don't know why it is, but guess that there is a precedence for setting the Background, causing the issue. Here is Dependency Property Value Precedence from MSDN, and I understand the precedence of of that reference, but I cannot link this issue to the precedence(MSDN).
Additionally, at above the code snippets, if the Grid is replaced with a Button, both these code will not be executed as expected.
UPDATED: Adding Button case about this issue
<Button>
<Button.Style >
<Style TargetType="{x:Type Button}">
<Setter Property="Background" Value="Transparent"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="Red"/>
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
The problem is your Grid has a null background, so it is not visible for mouse hit testing. If you set the Background to Transparent, then it will be hit-testable like when you set it to green.
More information can be found here.
I tried your Button and the Background is initialized to Transparent properly. The Red value is only shown for a fraction of a second. This is because in the Aero theme (I'm on Windows 7) the Button's ControlTemplate uses a custom chrome to provide animated state transitions (i.e. when hovering etc). This custom chrome element uses internal brushes and it ignores the Background property.
This has nothing to do with property precedence. For the Grid, it's simply a matter of your Grid not being hit-testable. So it's IsMouseOver will not be set to true, unless it has a non-null background (or a child which renders something).
You can see the Precedence in action here:
<Grid Background="Blue">
<Grid.Style>
<Style TargetType="{x:Type Grid}">
<Setter Property="Background" Value="Green"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="Red"/>
</Trigger>
</Style.Triggers>
</Style>
</Grid.Style>
</Grid>
In the above, the Grid will always be Blue as that has the highest precedence (i.e. local or #3). While Red (#6) takes precedence over Green (#8).
In the case of the Button, you have something like this:
<Button Background="Blue">
<Button.Style>
<Style TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border x:Name="border" Background="{TemplateBinding Background}">
<ContentPresenter />
</Border >
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="border" Property="Background" Value="Red"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Button.Style>
</Button>
In this case, there are two Background properties in play: the Button and the Border in the control template. The Button's Background property is used by the Border by default, but when the mouse is hovering it uses a Red brush. At that point, it doesn't matter what the value of the Button's Background property is set to.

Resources