WPF Mouseover Trigger Effect for Child Controls - wpf

Lets say I have this bit of code:
<Window>
<Window.Resources>
<Color x:Key="MyColor"
A="255"
R="152"
G="152"
B="152" />
<DropShadowEffect x:Key="MyEffect"
ShadowDepth="0"
Color="{StaticResource MyColor}"
BlurRadius="10" />
<Style x:Key="MyGridStyle"
TargetType="{x:Type Grid}">
<Setter Property="Height"
Value="200" />
<Setter Property="Width"
Value="200" />
<Style.Resources>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Width"
Value="100" />
</Style>
<Style TargetType="{x:Type Image}">
<Setter Property="Height"
Value="100" />
<Setter Property="Width"
Value="100" />
</Style>
</Style.Resources>
<Style.Triggers>
<Trigger Property="IsMouseOver"
Value="true">
<!-- How do I apply my effect when this grid is hovered over to Image and TextBox, but not the grid itself? -->
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid Style="{StaticResource MyGridStyle}">
<Grid.RowDefinitions>
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Image Grid.Row="0"
Grid.Column="0"
Source="image.png" />
<TextBlock Grid.Row="0"
Grid.Column="0"
Text="Hover Over Me" />
</Grid>
</Window>
Basically I have a Style applied to the Grid that says any TextBlock or Image within it should be styles to a certain size.
I want to create a Trigger on the Grid that causes an effect to be applied to all TextBlocks and Images within the Grid, but not to the Grid itself.
I can apply the Trigger directly to TextBlock and/or Image, but then the effect only occurs on each element separately. I need to have the effect occur to any TextBlock and/or Image within the Grid despite which inner child element I am hovered over.
Can anyone help me with this?

You can do it the other way around. That is, add DataTriggers to Image and TextBlock and make them trigger on IsMouseOver for the ancestor Grid.
Note: If you want this effect to trigger as soon as the mouse is over the Grid you will need to set Background to a value, like Transparent. By default, the Background is null and this value isn't used in hit testing.
<Style x:Key="MyGridStyle" TargetType="{x:Type Grid}">
<!--<Setter Property="Background" Value="Transparent"/>-->
<Setter Property="Height" Value="200" />
<Setter Property="Width" Value="200" />
<Style.Resources>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Width" Value="200" />
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=Grid},
Path=IsMouseOver}" Value="True">
<Setter Property="Effect" Value="{StaticResource MyEffect}"/>
</DataTrigger>
</Style.Triggers>
</Style>
<Style TargetType="{x:Type Image}">
<Setter Property="Height" Value="200" />
<Setter Property="Width" Value="200" />
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=Grid},
Path=IsMouseOver}" Value="True">
<Setter Property="Effect" Value="{StaticResource MyEffect}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Style.Resources>
</Style>

We once had a similar requirement of outer glowing ONLY the content of a row of a list box, not the row overall. We took help of this article... http://drwpf.com/blog/2008/03/25/itemscontrol-i-is-for-item-container.

Related

WPF DockPanel IsMouseOver only triggered when child controls are moused over

I have a sort of tile system for my application. See the below code:
<WrapPanel Grid.Column="0" Grid.Row="1">
<DockPanel Style="{StaticResource Panel}">
<Label Content="Upload"/>
<Image Width="40">
<Image.Source>
<BitmapImage DecodePixelWidth="40" UriSource="images/download.png" />
</Image.Source>
</Image>
</DockPanel>
</WrapPanel>
As you can see, I have got the main container ([icode]WrapPanel[/icode]), and then I have got multiple [icode]DockPanel[/icode]'s which makeup the tile itself.
For some reason when I mouseover the DockPanel, the IsMouseOver trigger doesn't trigger, but it does when I mouse over any of it's children. Once triggered it remains triggered until my mouse leaves the DockPanel.
Here is the style:
<Style x:Key="Panel" TargetType="DockPanel">
<Setter Property="Margin" Value="4" />
<Setter Property="Cursor" Value="Hand" />
<Setter Property="Height" Value="118" />
<Setter Property="Width" Value="118" />
<Setter Property="LastChildFill" Value="True" />
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#FF212121" />
</Trigger>
</Style.Triggers>
<Style.Resources>
<Style TargetType="Label">
<Setter Property="Foreground" Value="White" />
<Setter Property="HorizontalAlignment" Value="Center" />
<Setter Property="VerticalAlignment" Value="Bottom" />
<Setter Property="Margin" Value="3" />
<Setter Property="DockPanel.Dock" Value="Bottom" />
</Style>
</Style.Resources>
</Style>
Any ideas?
Try to set DockPanel's background to Transparent.
When background is null, WPF will not include it in hit test.

Background color is not changing in DataTrigger

I have some problem while changing background color of Button using datatrigger but it is not happen.
My code is this
<Window x:Class="DataBinding.DataTrigger2"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="DataTrigger2" Height="300" Width="300">
<Button Height="50" Name="btn">
<Button.Style>
<Style TargetType="Button">
<Setter Property="Control.Background" Value="YellowGreen"></Setter>
<Setter Property="Control.Foreground" Value="Brown"></Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=btn, Path=IsPressed}" Value="True">
<Setter Property="Control.Background" Value="Purple"></Setter>
<Setter Property="Control.Foreground" Value="Red"></Setter>
<Setter Property="Control.FontSize" Value="20"></Setter>
<Setter Property="Control.FontWeight" Value="Light"></Setter>
<Setter Property="Control.Width" Value="150"></Setter>
<Setter Property="Control.Height" Value="50"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
<Button.Content>
The Gaming World Inc.
</Button.Content>
</Button>
The reason why you don't see the Button.Background colour change when you click the mouse is because of the default ControlTemplate that defines what the it should look like in different states, eg. pressed, disabled. etc. These are defined in the VisualStateManager.VisualStateGroups section of the ControlTemplate and they potentially override your Background changes.
As simple proof of this, we can provide a basic ControlTemplate for the Button without the VisualStateManager.VisualStateGroups section and then you will see your Background colour change. Try this:
<Button Height="50" Name="btn">
<Button.Style>
<Style TargetType="Button">
<Setter Property="Control.Background" Value="YellowGreen"></Setter>
<Setter Property="Control.Foreground" Value="Brown"></Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Background="{TemplateBinding Background}">
<ContentPresenter />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=btn, Path=IsPressed}" Value="True">
<Setter Property="Control.Background" Value="Purple"></Setter>
<Setter Property="Control.Foreground" Value="Red"></Setter>
<Setter Property="Control.FontSize" Value="20"></Setter>
<Setter Property="Control.FontWeight" Value="Light"></Setter>
<Setter Property="Control.Width" Value="150"></Setter>
<Setter Property="Control.Height" Value="50"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
<Button.Content>
The Gaming World Inc.
</Button.Content>
</Button>
You can find the original/default ControlTemplate for the Button control in the Button Styles and Templates page on MSDN... you may want to copy more of that ControlTemplate into your one to make your Button look more like the original one.

Resizing only open expanders

I have 3 expanders in a window and I want to know if there is a way to have it so that if I have expander 2 and 3 open but not 1 it will only stretch those 2. It's current stretching all three equally. Also, when they are closed the content closes but the column stays the same width as if it was open.
What is the best way to get them to only stretch when expanded and go back to the small size of 10 pixels wide when collapsed?
You could set Triggers on the ColumnDefinitions to set the Width to Fixed when the Expander is closed and set to proportional when the Expander is open.
Example:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition>
<ColumnDefinition.Style>
<Style TargetType="ColumnDefinition">
<Setter Property="Width" Value="20" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsExpanded, ElementName=expander1}" Value="True">
<Setter Property="Width" Value="20*" />
</DataTrigger>
</Style.Triggers>
</Style>
</ColumnDefinition.Style>
</ColumnDefinition>
<ColumnDefinition>
<ColumnDefinition.Style>
<Style TargetType="ColumnDefinition">
<Setter Property="Width" Value="20" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsExpanded, ElementName=expander2}" Value="True">
<Setter Property="Width" Value="20*" />
</DataTrigger>
</Style.Triggers>
</Style>
</ColumnDefinition.Style>
</ColumnDefinition>
<ColumnDefinition>
<ColumnDefinition.Style>
<Style TargetType="ColumnDefinition">
<Setter Property="Width" Value="20" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsExpanded, ElementName=expander3}" Value="True">
<Setter Property="Width" Value="20*" />
</DataTrigger>
</Style.Triggers>
</Style>
</ColumnDefinition.Style>
</ColumnDefinition>
</Grid.ColumnDefinitions>
<Expander Name="expander1" Grid.Column="0" Header="Expander1" />
<Expander Name="expander2" Grid.Column="1" Header="Expander2" />
<Expander Name="expander3" Grid.Column="2" Header="Expander3" />
</Grid>
Result:

Apply style to multiple controls when a style trigger fires in xaml

When the user enters info I want all the controls to look and act the same way, as a result I have done the following.
The label and textbox controls are in a stackpanel as:
<StackPanel Style="{StaticResource ResourceKey=myInput}" HorizontalAlignment="Left">
<Label Content="Label" Name="label1" />
<TextBox Name="textBox1" ></TextBox>
</StackPanel>
the style "myInput" is:
<Style x:Key="myInput" TargetType="{x:Type StackPanel}">
<Style.Resources>
<Style TargetType="{x:Type Label}" BasedOn="{StaticResource {x:Type Label}}">
<Setter Property="FontSize" Value="12" />
</Style>
<Style TargetType="{x:Type TextBox}">
<Setter Property="Margin" Value="5,-5,2,2"></Setter>
<Setter Property="Height" Value="23"/>
<Setter Property="VerticalAlignment" Value="Top"/>
<Style.Triggers>
<Trigger Property="IsFocused" Value="true">
<Setter Property="Background" Value="Blue" >
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</Style.Resources>
</Style>
Now every time that I apply that style to a stackpanel that has a label and a textbox the background of the textbox changes to blue when it receives focus.
How could I set the label's fontweight to bold also when that event fires? I will like to do this with xaml.
Use a DataTrigger on your Label which looks to see if the keyboard focus is inside of the parent StackPanel that contains both objects
<Style TargetType="{x:Type Label}" BasedOn="{StaticResource {x:Type Label}}">
<Setter Property="FontSize" Value="12" />
<Style.Triggers>
<DataTrigger Value="True" Binding="{Binding IsKeyboardFocusWithin,
RelativeSource={RelativeSource AncestorType={x:Type StackPanel}}}">
<Setter Property="FontWeight" Value="Bold" />
</Trigger>
</Style.Triggers>
</Style>

How to parametrize WPF Style?

I'm looking for a simplest way to remove duplication in my WPF code.
Code below is a simple traffic light with 3 lights - Red, Amber, Green. It is bound to a ViewModel that has one enum property State taking one of those 3 values.
Code declaring 3 ellipses is very duplicative. Now I want to add animation so that each light fades in and out - styles will become even bigger and duplication will worsen.
Is it possible to parametrize style with State and Color arguments so that I can have a single style in resources describing behavior of a light and then use it 3 times - for 'Red', 'Amber' and 'Green' lights?
<UserControl.Resources>
<l:TrafficLightViewModel x:Key="ViewModel" />
</UserControl.Resources>
<StackPanel Orientation="Vertical" DataContext="{StaticResource ViewModel}">
<StackPanel.Resources>
<Style x:Key="singleLightStyle" TargetType="{x:Type Ellipse}">
<Setter Property="StrokeThickness" Value="2" />
<Setter Property="Stroke" Value="Black" />
<Setter Property="Height" Value="{Binding Width, RelativeSource={RelativeSource Self}}" />
<Setter Property="Width" Value="60" />
<Setter Property="Fill" Value="LightGray" />
</Style>
</StackPanel.Resources>
<Ellipse>
<Ellipse.Style>
<Style TargetType="{x:Type Ellipse}" BasedOn="{StaticResource singleLightStyle}">
<Style.Triggers>
<DataTrigger Binding="{Binding State}" Value="Red">
<Setter Property="Fill" Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
</Ellipse.Style>
</Ellipse>
<Ellipse>
<Ellipse.Style>
<Style TargetType="{x:Type Ellipse}" BasedOn="{StaticResource singleLightStyle}">
<Style.Triggers>
<DataTrigger Binding="{Binding State}" Value="Amber">
<Setter Property="Fill" Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
</Ellipse.Style>
</Ellipse>
<Ellipse>
<Ellipse.Style>
<Style TargetType="{x:Type Ellipse}" BasedOn="{StaticResource singleLightStyle}">
<Style.Triggers>
<DataTrigger Binding="{Binding State}" Value="Green">
<Setter Property="Fill" Value="Green" />
</DataTrigger>
</Style.Triggers>
</Style>
</Ellipse.Style>
</Ellipse>
</StackPanel>
As long as your "Traffic Light" is wrapped up inside a control, which it appears it is, I don't think this is horrible. Each ellipse is well defined and has different triggers, each indicating its own state. You've already factored the common parts out into the base style, which is good.
You could wrap the individual ellipses inside another user control (which wouldn't need a backing ViewModel) that had an ActiveState property and an ActiveFill property. Then your TrafficLight looks something like:
<StackPanel Orientation="Vertical" DataContext="{StaticResource ViewModel}">
<my:Indicator State="{Binding State}" ActiveState="Red" ActiveFill="Red" />
<my:Indicator State="{Binding State}" ActiveState="Amber" ActiveFill="Red" />
<my:Indicator State="{Binding State}" ActiveState="Green" ActiveFill="Green" />
</StackPanel>
This lets you wrap up all your Ellipse styling inside your Indicator control and the only thing that control needs to worry about is comparing the State to the ActiveState to determine if it should fill itself with the ActiveFill brush.
As to if this is worth the effort or not, that depends on how many of these you have floating around and if you use them outside of your Traffic Light user control. Remember: You Ain't Gonna Need It.

Resources