How to change multiple properties on mouseover - wpf

I've just started with WPF (I'm sorry if the question is too obvious), and I managed to put together this mouseover style. The background color animates to a darker color. I now want to also animate the text to white, so it's easier to read.
This is how I tried to add it, but it gives me the error "Cannot resolve all property references in the property path 'TextBlock.Foreground'. Verify that applicable objects support the properties" when I mouseover it.
<Border Background="#e6ebf3" CornerRadius="0,10,0,10" >
<Border.Style>
<Style TargetType="Border">
<Setter Property="Background" Value="#e6ebf3" />
<Setter Property="TextBlock.Foreground" Value="Black"/>
<Style.Triggers>
<EventTrigger RoutedEvent="UIElement.MouseEnter">
<BeginStoryboard>
<Storyboard>
<ColorAnimation Duration="0:0:0.1" Storyboard.TargetProperty="Background.Color" To="#6d809b" />
<ColorAnimation Duration="0:0:0.1" Storyboard.TargetProperty="TextBlock.Foreground" To="white" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="UIElement.MouseLeave">
<BeginStoryboard>
<Storyboard>
<ColorAnimation Duration="0:0:0.1" Storyboard.TargetProperty="Background.Color" To="#e6ebf3" />
<ColorAnimation Duration="0:0:0.1" Storyboard.TargetProperty="TextBlock.Foreground" To="Black" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Style.Triggers>
</Style>
</Border.Style>........

I found an alternative way without using storyboard or animations, so I'll post it just in case. Still wondering about the original one, though.
<Border CornerRadius="0,10,0,10" >
<Border.Style>
<Style TargetType="Border">
<Setter Property="Background" Value="#e6ebf3" />
<Setter Property="TextBlock.Foreground" Value="Black"/>
<Style.Triggers>
<Trigger Property ="IsMouseOver" Value="True">
<Setter Property= "Background" Value="#6d809b"/>
<Setter Property= "TextBlock.Foreground" Value="White"/>
</Trigger>
</Style.Triggers>
</Style>
</Border.Style>
...

Indirect property targeting, that being TextBlock.Foreground is described here http://msdn.microsoft.com/en-us/library/ms742451.aspx. It's basically saying, "hey I couldn't find a property called TextBlock on type button." It works with Background.Color because the Background Property does exist on Button and it's of type ColorBrush which itself has a property of type Color.

Related

WPF Animation To dynamic value

I want show up a UserControl occasionally.
When the visibility of the UserControl is changed to visible, it should ease in in a moderate speed.
I figured out how to to this basically:
<UserControl.Resources>
<Style TargetType="{x:Type UserControl}" x:Key="MyStyleName">
<Style.Triggers>
<Trigger Property="Visibility" Value="Visible">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Height" From="0.0" To="200.0" Duration="0:0:1" />
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
</Style.Triggers>
</Style>
</UserControl.Resources>
This works fine.
In the real world application, I don't know the exact value of the Height property, because the UserControl is "hosted" in a Grid and the row height is set to "Auto".
I've tried to do a Binding to the TemplatedParent, doesn't work. A Binding to a RelativeSource doesn't work either.
Can anyone help me, please?
Thanks!
As an alternative, animate a ScaleTransform in the control's RenderTransform:
<Style TargetType="UserControl">
<Setter Property="RenderTransformOrigin" Value="0.5,0.5"/>
<Setter Property="RenderTransform">
<Setter.Value>
<ScaleTransform ScaleY="0"/>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="Visibility" Value="Visible">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation To="1" Duration="0:0:1"
Storyboard.TargetProperty="RenderTransform.ScaleY"/>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
</Style.Triggers>
</Style>

Animate Grid Background color

I want to change the grid background via binding, if a condition is true.
I am using the MVVM light framework.
<Grid.Style>
<Style TargetType="Grid">
<Style.Triggers>
<DataTrigger Binding="{Binding SuccessBooked, UpdateSourceTrigger=PropertyChanged}"
Value="True">
<!--Setter Property="Background" Value="LimeGreen" />-->
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation To="LimeGreen"
Storyboard.TargetName="ActualWeightBg"
Storyboard.TargetProperty="Background"
FillBehavior="Stop"
Duration="0:0:12"/>
<!--<DoubleAnimation Storyboard.TargetProperty="Opacity"
From="0.0" To="1.0" Duration="0:0:15"/>-->
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
<DataTrigger Binding="{Binding LowerBooked, UpdateSourceTrigger=PropertyChanged}"
Value="True">
<Setter Property="Background" Value="Red" />
</DataTrigger>
<DataTrigger Binding="{Binding HigherBooked, UpdateSourceTrigger=PropertyChanged}"
Value="True">
<Setter Property="Background" Value="DarkOrange" />
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Style>
The compiler complains:
If I remove Storyboard.TargetName="ActualWeightBg":
<ColorAnimation To="LimeGreen"
Storyboard.TargetProperty="Background"
FillBehavior="Stop"
Duration="0:0:12"/>
I get this exception:
Exception thrown: 'System.InvalidOperationException' in PresentationFramework.dll
I want that background color is going to change for 5s, after that it should change back to the standard color.
Update
So when a background color is not set, it will throw the error:
Cannot resolve all property references in the property path
'Background.Color'. Verify that applicable objects support the
properties.
As you see here, the background color is not set
then the error will occur:
But when the background is set:
Then it will work as expected.
How can I set an animated background color, without background color being set.
It is an usercontrol.
First of all, StyleTriggers in general dont support TargetNames.
AFAIK only TemplateTriggers do support them.
Your DataTrigger should look like this.
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation Storyboard.TargetProperty="Background.Color" To="LimeGreen"
FillBehavior="Stop" AutoReverse="True" Duration="0:0:5" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
Notice the Storyboard.TargetProperty="Background.Color". Also your TimeSpan was too high. Another thing is setting the AutoReverse to true.
EDIT
To get this Trigger to work, in the Grid's Style there has an initial Background to be set.
<Style TargetType="Grid">
<Setter Property="Background" Value="Red"/>

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.

How to prevent WPF trigger from resetting original value upon exit

I have a mouseover-selecting listview by using trigger:
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="IsSelected" Value="True" />
</Trigger>
</Style.Triggers>
</Style>
</ListView.ItemContainerStyle>
However when mouse is moved out, the trigger sets IsSelected back to false, which is not desired here. Is there a way to make a trigger only set-upon-enter but not reset-upon-exit?
This is what EventTriggers are for:
Unlike Trigger, EventTrigger has no concept of termination of state, so the action will not be undone once the condition that raised the event is no longer true.
In your case for example:
<EventTrigger RoutedEvent="MouseEnter">
<BeginStoryboard>
<Storyboard>
<BooleanAnimationUsingKeyFrames Storyboard.TargetProperty="IsSelected">
<DiscreteBooleanKeyFrame KeyTime="0" Value="True" />
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>

WPF - Best way of responding to changes in a ViewModel at Page/Window level

I'm developing an XBAP and i have a simple requirement.
The DataContext of the whole main page is set to an instance of my UserViewModel. The UserViewModel has a DependencyProperty called AuthenticationState which is an enum with values like 'Authenticated','NotAutheticated' and 'AuthenticationFailed'.
Now, i need to respond to any change in this value by hiding/displaying various elements on the page.
What (and where) is the best way of doing that?
As you mentioned you can't use a DataTrigger directly on a control. A work around would be to use a style on each Control that needs to be hidden.
<Grid>
<Rectangle Fill="Red" />
<Grid.Style>
<Style TargetType="Grid">
<Style.Triggers>
<DataTrigger Binding="{Binding Test}" Value="true">
<Setter Property="Visibility" Value="Collapsed" />
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Style>
</Grid>
A preferable method would be to use a Converter called "AuthenticationStateToVisibilityConverter" that is used in binding the control's Visibility property to the data context's AuthenticationState property.
The best way would be to use a DataTrigger. So something like this:
<Window.Triggers>
<DataTrigger Binding="{Binding AuthenticationState}" Value="NotAuthenticated">
<Setter TargetName="nameOfControl" Property="Visibility" Value="Collapsed" />
</DataTrigger>
...
<TextBox x:Name="nameOfControl" />
</Window.Triggers>
As long as you UserViewModel object is in the DataContext of the Window then this should work!
Managed to sort it using styles. It's a pain but it works!
The full source is below.
<Grid x:Name="contentGrid" Grid.Row="1">
<!--login-->
<controls:LoginControl>
<controls:LoginControl.Style>
<Style>
<Setter Property="Control.Opacity" Value="0"/>
<Setter Property="Control.IsHitTestVisible" Value="False"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Source={StaticResource UserViewModel},Path=UserAuthenticationState}"
Value="{x:Static model:AuthenticationState.NotAuthenticated}">
<Setter Property="Control.IsHitTestVisible" Value="True"/>
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation To="1" Duration="0:0:2"
Storyboard.TargetProperty="Opacity"></DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation To="0" Duration="0:0:2"
Storyboard.TargetProperty="Opacity"></DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</controls:LoginControl.Style>
</controls:LoginControl>
<!--slider-->
<slider:PageSlider>
<Button>1</Button>
<Button>2</Button>
<Button>3</Button>
<slider:PageSlider.Style>
<Style>
<Setter Property="Control.Opacity" Value="0"/>
<Setter Property="Control.IsHitTestVisible" Value="False"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Source={StaticResource UserViewModel},Path=UserAuthenticationState}"
Value="{x:Static model:AuthenticationState.Authenticated}">
<Setter Property="Control.IsHitTestVisible" Value="True"/>
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation To="1" Duration="0:0:2"
Storyboard.TargetProperty="Opacity"></DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation To="0" Duration="0:0:2"
Storyboard.TargetProperty="Opacity"></DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</slider:PageSlider.Style>
</slider:PageSlider>
</Grid>
Actually, the best way to do this is to expose the appropriate properties from your view model. This makes your logic more centralized and easier to test. Also, it performs better than converters. It is, after all, a view model. Therefore, it should model the view. If the view needs a property to tell it when to hide / show a panel, add such a property to your view model.

Resources