WPF - How to write a trigger for mouse over of grid? - wpf

I see that Button object has a IsMouseOVer property.
But how do create an effect for the mouse over of a grid or other
element that does not have IsMouseOver??
Thanks
Malcolm
Edit: i figured it out. I was using the wrong method for setting the trigger.

I realize that I am responding to a dead thread but since I came across it, and since the thread is not answered, I am going to answer it.
The WPF Grid has an "IsMouseOver" property.
I think this question was asked because the "IsMouseOver" property only changes if the mouse is over a "hit-testable" control (ie a Button, or ComboBox) within the Grid itself.
Therefore, it may appear that the "IsMouseOver" property doesn't work (especially if you are using it in a trigger to set the Grid's Visible property).
For example:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="25" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Button Grid.Column="1">A Button</Button>
<Grid.Style>
<Style TargetType="{x:Type Grid}">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="False">
<Setter Property="Opacity" Value="0.5"></Setter>
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Opacity" Value="1"></Setter>
</Trigger>
</Style.Triggers>
</Style>
</Grid.Style>
</Grid>
The above Grid and it's contents will be displayed in half opacity.
You will notice that the opacity will not be set to full if you hover over the first column (which doesn't contain anything); however the opacity will be set to full if you hover over the button in the second column. This is because the first column, with nothing in it, is not hit-testable; whereas, the button within the second column is hit-testable and so the event is triggered.
If you want the Grid's IsMouseOver property to detect when the mouse is anywhere over the Grid itself all you have to do is set the Background property of the Grid to something that is not Null (set it to Transparent for example). Setting the Background property will make the Grid "hit-testable".
The following code will fix the problem:
<Grid Background="Transparent">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="25" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Button Grid.Column="1">A Button</Button>
<Grid.Style>
<Style TargetType="{x:Type Grid}">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="False">
<Setter Property="Opacity" Value="0"></Setter>
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Opacity" Value="1"></Setter>
</Trigger>
</Style.Triggers>
</Style>
</Grid.Style>
</Grid>
-Frinny

One can also use the MouseEnter/MouseLeave events as such:
<Grid Name="grid">
<Grid.Triggers>
<EventTrigger RoutedEvent="MouseEnter">
<BeginStoryboard>
<Storyboard>
<ThicknessAnimation Storyboard.TargetProperty="Margin" From="0,0,-300,0" To="0,0,0,0" DecelerationRatio=".9" Duration="0:0:1" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="MouseLeave">
<BeginStoryboard>
<Storyboard>
<ThicknessAnimation Storyboard.TargetProperty="Margin" From="0,0,0,0" To="0,0,-300,0" AccelerationRatio=".9" Duration="0:0:1" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Grid.Triggers>
</Grid>

That would be the "MouseEnter" and "MouseLeave" actions on the WPF object.

Related

im trying to slide out and in a menubar with button click WPF

here is what im doing
i have a Buttons.xaml style file for styling my menu button
<!-- Menu button -->
<Style x:Key="MenuButton" TargetType="{x:Type Button}">
<Setter Property="Background" Value="{x:Null}" />
<Setter Property="BorderBrush" Value="{x:Null}" />
<Setter Property="Height" Value="20" />
<Setter Property="Width" Value="50" />
<Setter Property="Margin" Value="0" />
<Setter Property="HorizontalAlignment" Value="Center" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="IsHitTestVisible" Value="True" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border x:Name="border">
<Grid VerticalAlignment="{TemplateBinding VerticalAlignment}"
HorizontalAlignment="{TemplateBinding HorizontalAlignment}">
<Rectangle x:Name="rectangle" Width="20" Height="2" Fill="{StaticResource DarkGrayBrush}" Margin="0 0 0 0" HorizontalAlignment="Center" VerticalAlignment="Top" RenderTransformOrigin="-0, 0.5" />
<Rectangle x:Name="rectangle1" Width="20" Height="2" Fill="{StaticResource DarkGrayBrush}" Margin="0 5 0 0" HorizontalAlignment="Center" VerticalAlignment="Top" RenderTransformOrigin="-0, 0.5" />
<Rectangle x:Name="rectangle2" Width="20" Height="2" Fill="{StaticResource DarkGrayBrush}" Margin="0 10 0 0" HorizontalAlignment="Center" VerticalAlignment="Top" RenderTransformOrigin="-0, 0.5" />
<ContentPresenter />
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" TargetName="border" Value="{StaticResource FaintWhiteBrush}"/>
</Trigger>
<EventTrigger RoutedEvent="Button.Click" SourceName="menuButton">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="gridMenu" Storyboard.TargetProperty="Width" From="0" To="100" Duration="0:0:0.3" />
</Storyboard>
</BeginStoryboard>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="gridMenu" Storyboard.TargetProperty="Width" From="100" To="0" Duration="0:0:0.3" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
and here is my usercontrol view file
that has menu button itself with the styling and source name in my style that is supposed to be found by my event trigger when this button is clicked and slide out the grid
but its not finding it
<Grid>
<!-- Menu button -->
<Button x:Name="menuButton" Style="{StaticResource MenuButton}" />
<!-- Menu bar -->
<Grid x:Name="gridMenu" Background="White" Width="0" HorizontalAlignment="Left">
</Grid>
when i run this code above this is the error message i get
InvalidOperationException: Cannot find element 'menuButton' targeted by this EventTrigger.
thanks in advance
Triggers inside ControlTemplates (or DataTemplates) can only reference elements that are inside that template. The template doesn't "know" about anything outside of itself, so it can't interact with anything but its own parts.
If you want menuButton to interact with gridMenu, that has to be set up from a context/scope that knows about both of those things. In your case, this would be the UserControl where they are both declared.
If you wanted to implement something like this using an EventTrigger (with XAML only- no code), it would have to be done in the ControlTemplate of the Control that directly contained those two elements. Something like this:
<Control>
<Control.Template>
<ControlTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Button Name="menuButton"/>
<Grid Name="gridMenu" Background="Green" Grid.Row="1"/>
</Grid>
<ControlTemplate.Triggers>
<EventTrigger SourceName="menuButton" RoutedEvent="Button.Click">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="gridMenu" Storyboard.TargetProperty="Width" From="0" To="100" Duration="0:0:0.3" />
</Storyboard>
</BeginStoryboard>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="gridMenu" Storyboard.TargetProperty="Width" From="100" To="0" Duration="0:0:0.3" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Control.Template>
</Control>
The above is an example showing how to place a trigger in a scope that contains both elements, so that one can affect another. It's not a finished solution, but it should give you an idea of what I mean.
Keep in mind that I've just copy/pasted your EventTriggers into my code. The triggers as you've written them only grow and then immediately shrink menuGrid, which I'm guessing isn't exactly what you want. You probably want it to grow, stay open, and then shrink at some later point. You'll need to work that out on your own (or ask another question), but assuming you want a second click of menuButton to close menuGrid, try using a ToggleButton instead of a normal Button and using EventTriggers in the Checked and Unchecked events.
One last thought: since you're making a UserControl you might want to consider declaring a Boolean property such as IsOpen and using that to trigger the menuGrid animation (via a DataTrigger) instead of having it triggered directly by Button click. This would let you open/close the menu from the Window that is housing the UserControl. All standard controls that have some sort of popup (such as ComboBox and Expander) have some such property that tells you the current state and allows you to change it from the outside.

Setting a button's selected color and focus in WPF

I have a button that has a transparent background and a thick white border. When the button detects a mouse enter I see that the background went from the set transparent background to it's default focused color so for that I just set the opacity to 0.2 so that it shows some feed back that it has a focus.
Now here is my dilemma. When I leave the button's bounds there is a small animation that goes from my .2 opacity to 1 before changing the background to the original transparent. I would like to know how to replace this with either a smoother animation so that I don't see the opaque background before it changing to transparent or just have a way to bypass the animation completely and just have it set my values. I see the similar thing happen when the button is focused. It will animate between 0.2 opacity and 1.0 with the default background color. Any ideas would be greatly appreciated.
<Button BorderBrush="White" BorderThickness="1" Width="45" Height="45" >
<Button.Style>
<Style TargetType="{x:Type Button}">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Opacity" Value="0.2" />
</Trigger>
<Trigger Property="IsMouseOver" Value="False">
<Setter Property="Background" Value="Transparent" />
<Setter Property="Opacity" Value="1.0" />
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
Here is a XAML only solution, I think this does what you want...
<Button BorderBrush="White" BorderThickness="1" Width="45" Height="45" Content="1234" >
<Button.Style>
<Style TargetType="{x:Type Button}">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="False">
<Setter Property="Background" Value="Transparent" />
<Setter Property="Opacity" Value="1.0" />
</Trigger>
<EventTrigger RoutedEvent="MouseLeave">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity" From="0.2" To="1"></DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="MouseEnter">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity" From="0.2" To="0.2"></DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>

Animate background colour on exitaction

I'm trying to animate the background colour on a grid to change, once an event happens, but I can't get it working, I can get it to change colour immediately (via data triggers), but as soon as I try to introduce an animation into it, then I can't get it working (the animation doesn't seem to come into effect).
This is the current XAML I'm using (though I've tried various variations and cannot get it to animate):
<DataTrigger Binding="{Binding ElementName=me, Path=Viewed}" Value="False">
<Setter Property="Background" Value="LightYellow" />
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation Duration="00:00:02" To="White" Storyboard.TargetProperty="(Grid.Background).(SolidColorBrush.Color)"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
<!--
<DataTrigger Binding="{Binding ElementName=me, Path=Viewed}" Value="True">
<Setter Property="Background" Value="White" />
</DataTrigger>
-->
Where Viewed is a dependency property (bool) on my Control. Any hints in the right direction would be appreciated. I've also tried setting it as an EventTrigger on a raised event which happens when the bool switches to true.
Thanks to Clemens helps, figured out what I needed to do:
<SolidColorBrush x:Key="GridColourBrush" Color="LightYellow" />
<Style x:Key="GridStyle" TargetType="Grid">
<Setter Property="Background" Value="White" />
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=me, Path=Viewed}" Value="False">
<Setter Property="Background" Value="{StaticResource GridColourBrush}" />
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation Duration="00:00:02" To="White" Storyboard.TargetProperty="(Grid.Background).(SolidColorBrush.Color)"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
<!-- snipped stuff -->
<Grid MinWidth="525" x:Name="ContainerGrid" Style="{StaticResource GridStyle}" Background="{StaticResource GridColourBrush}" />
So setting the background to be flat white by default, then if the DP bool is false, change the background to the static solid colour brush, which I can then animate via the exit actions.
What i meant was simply that instead of
<Grid Background="LightYellow">
</Grid>
you would have to write
<Grid>
<Grid.Background>
<SolidColorBrush Color="LightYellow" />
</Grid.Background>
</Grid>
No need to have an extra resource.

Use style trigger to set property of a nested object

I have a small polygon written on the large canvas. I want to highlight a polygon when mouse is moving over the canvas. The code is like this:
<UserControl ...>
<Canvas Name="canvas" Height="22" Width="22">
<Canvas.Resources>
<Style TargetType="Canvas">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="false">
<Setter Property="polygon.Stroke" Value="#EEEEEE"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="polygon.Stroke" Value="Aqua"/>
</Trigger>
</Style.Triggers>
</Style>
</Canvas.Resources>
<Polygon Points="11,1 16,6 16,16 11,21" Name="polygon">
<Polygon.Fill>
<SolidColorBrush Color="#EEEEEE"/>
</Polygon.Fill>
</Polygon>
</Canvas>
</UserControl>
However setter does not see the "polygon".
You cannot use Setters like that, if you use this kind of notation the engine will look for an attached property, or if no Style.TargetType was set for a property on the type before the dot.
The easiest thing to do is probably applying a style to the polygon itself and using a DataTrigger which binds to the Canvas so you can trigger on its properties.
<Polygon Points="11,1 16,6 16,16 11,21" Name="polygon">
<Polygon.Fill>
<SolidColorBrush Color="#EEEEEE"/>
</Polygon.Fill>
<Polygon.Style>
<Style TargetType="{x:Type Polygon}">
<Style.Triggers>
<DataTrigger
Binding="{Binding Path=IsMouseOver,
RelativeSource={RelativeSource
AncestorType={x:Type Canvas}}}"
Value="True">
<Setter Property="Stroke" Value="Red"/>
</DataTrigger>
</Style.Triggers>
</Style>
<Polygon.Style>
</Polygon>
Try EventTrigger, because other kinds of triggers you could only use in templates or styles. And we already know that Style.Trigger doesn't allow your scenario. So here is working example for you:
<Canvas Name="canvas" Height="22" Width="22">
<Polygon Points="11,1 16,6 16,16 11,21" Name="polygon">
<Polygon.Fill>
<SolidColorBrush x:Name="brush" Color="#EEEEEE"/>
</Polygon.Fill>
<Polygon.Triggers>
<EventTrigger RoutedEvent="UIElement.MouseEnter">
<BeginStoryboard>
<Storyboard Storyboard.TargetName="brush" Storyboard.TargetProperty="Color">
<ColorAnimation From="#EEEEEE" To="Aqua" Duration="00:00:00.01" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="UIElement.MouseLeave">
<BeginStoryboard>
<Storyboard Storyboard.TargetName="brush" Storyboard.TargetProperty="Color">
<ColorAnimation From="Aqua" To="#EEEEEE" Duration="00:00:00.01" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Polygon.Triggers>
</Polygon>
</Canvas>
It's looking for a property of the Canvas called 'polygon', which in turn has a property called 'Stroke'. You need to use TargetName if you want the setter to target a different object.
<Setter TargetName="polygon" Property="Stroke" Value="#EEEEEE" />

WPF: Creating a ListView with expanding ListItems

So I want a list of items that when you select them they expand to show more info (no toggleButton).
I figure there are multiple ways to do this but what I started at was that I had a ListView bound to the collection of viewModels and then defined the ViewModel view as Expander. Problem here was binding the selected one to be expanded.
I started getting multiple ideas on how this could be done differently. Perhaps modding the ControlTemplate of the ListView to have it's items set as my own type of expander. But I'm not sure how well that works when the ItemsSource is set for the list.
Problem is I'm not too sure what the best way here is.
You can easily select the DataTemplate of the selected ListViewItem by setting ListView.ItemContainerStyle and using appropriate triggers.
Here's an example of how you can not only change the visual tree of the selected item, but also animate its properties at the same time as well.
<ListView ItemsSource="{Binding ...}">
<ListView.Resources>
<!-- this is what unselected items will look like -->
<DataTemplate x:Key="DefaultItemTemplate">
<TextBlock FontSize="12" Margin="0,0,10,0" Text="Unselected" />
</DataTemplate>
<DataTemplate x:Key="SelectedItemTemplate">
<Border BorderBrush="Red" BorderThickness="2" Padding="5">
<TextBlock FontSize="12" Margin="0,0,10,0" Text="Selected" />
</Border>
</DataTemplate>
</ListView.Resources>
<ListView.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<!-- set properties for all items -->
<Setter Property="Margin" Value="0,2,0,2" />
<Setter Property="Padding" Value="0,2,0,2" />
<Setter Property="ContentTemplate" Value="{StaticResource DefaultItemTemplate}" />
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<!-- change what the selected item looks like -->
<Setter Property="ContentTemplate" Value="{StaticResource SelectedItemTemplate}" />
<!-- animate it as well -->
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="MinHeight" To="80" Duration="0:0:1" />
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="MinHeight" To="0" Duration="0:0:1" />
</Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
</Trigger>
</Style.Triggers>
</Style>
</ListView.ItemContainerStyle>
</ListView>

Resources