WPF VisualStateManager Animation Binding - wpf

I'm using a simple DoubleAnimation in Button.Trigger. Animation From is bound to root element ActualHeight. This works as expected.
Next I've tried to move the Storyboard to VisualStateManager. Now WPF complains:
System.Windows.Data Error: 2 : Cannot find governing FrameworkElement or FrameworkContentElement for target element. BindingExpression:Path=ActualWidth; DataItem=null; target element is 'DoubleAnimation' (HashCode=29201322); target property is 'To' (type 'Nullable`1')
Is it not possible to use bindings in animations within the VisualStateManager.VisualStateGroups/VisualState ?
It seems not. I fixed the problem by moving the storyboard to resources. So now GameToTitle will work while TitleToGame will fail.
I'd still appreciate to know if this is expected or not.
Relevant XAML:
<Grid x:Name="MainInterface">
<Grid.Resources>
<Storyboard x:Key="GameToTitle">
<DoubleAnimation Storyboard.TargetName="GameScreenInterface" Storyboard.TargetProperty="(Control.RenderTransform).(TransformGroup.Children)[1].(TranslateTransform.Y)" From="0" To="{Binding Path=ActualHeight, ElementName=MainInterface}" Duration="0:0:0.2"/>
<DoubleAnimation Storyboard.TargetName="TitleScreenInterface" Storyboard.TargetProperty="(Control.RenderTransform).(TransformGroup.Children)[1].(TranslateTransform.Y)" From="{Binding Path=ActualHeight, ElementName=MainInterface, Converter={Converters:InverseConverter}}" To="0" Duration="0:0:0.2"/>
</Storyboard>
</Grid.Resources>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup>
<VisualState x:Name="TitleScreenState" Storyboard="{StaticResource ResourceKey=GameToTitle}"/>
<VisualState x:Name="GameScreenState">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="GameScreenInterface" Storyboard.TargetProperty="(Control.RenderTransform).(TransformGroup.Children)[1].(TranslateTransform.Y)" From="{Binding Path=ActualHeight, ElementName=MainInterface}" To="0" Duration="0:0:0.2"/>
<DoubleAnimation Storyboard.TargetName="TitleScreenInterface" Storyboard.TargetProperty="(Control.RenderTransform).(TransformGroup.Children)[1].(TranslateTransform.Y)" From="0" To="{Binding Path=ActualHeight, ElementName=MainInterface, Converter={Converters:InverseConverter}}" Duration="0:0:0.2"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<View:TitleScreen x:Name="TitleScreenInterface"/>
<View:GameScreen x:Name="GameScreenInterface" />
</Grid>

As the question's comments note, Storyboards containing bindings must be declared as resources instead of inline within the VisualStateGroup for the bindings to work. This is due to the fact that VisualStates aren't part of the VisualTree. Resources, on the other hand, are.
You can visualize this for yourself by using Snoop (or similar utility) to look at the visual tree of your application. You'll notice that Resources nodes appear in the visual tree within Snoop, but VisualStates do not.

Related

Add Color Animation to Mah:Tile

I'm Using MahApps.Metro and I'm using a Tile Element which Is loaded with namespace 'MahCtrl'( xmlns:MahCtrl="http://metro.mahapps.com/winfx/xaml/controls). I want to apply ColorAnimation to the Tile on MouseEnter and MouseLeave.
Here is the xaml snippet I am Currently working on.
<UserControl xmlns:MahCtrl="http://metro.mahapps.com/winfx/xaml/controls">
<MahCtrl:Tile Cursor="Hand" Background="Transparent" Height="200" Width="210"HorizontalContentAlignment="Center">
<MahCtrl:Tile.Triggers>
<EventTrigger RoutedEvent="MouseEnter">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation
Duration="0:0:0.200"
Storyboard.TargetProperty="(MahCtrl:Tile.Background).Color"
To="#fffccc" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
<EventTrigger RoutedEvent="MouseLeave">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation
Duration="0:0:0.250"
Storyboard.TargetProperty="(MahCtrl:Tile.Background).Color"
To="#ffffff" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</MahCtrl:Tile.Triggers>
</MahCtrl:Tile>
</UserControl>
In the debug section, When I Enter mouse into the Tile the following exception occurs
System.InvalidOperationException: 'Cannot resolve all property
references in the property path '(0).Color'. Verify that applicable
objects support the properties.'
I have tried Using (Background).Color and a lot of other combinations to StoryBoard.TargetProperty But this method works when MahCtrl:Tile element is wrapped with a stack panel and applying event triggers MouseEnter and MouseLeave triggers with target (StackPanel.Background).Color to the StackPanel. How can I target background property of MahCtrl:Tile
It would be a great help if someone can refer to a documentation regarding this topic
Thank you very much,

Cannot resolve all property references in the propertypath

I have a UserControl with a Border, the color of the border should be setted with a Dependency Property. I also want to animate the opacity of the border. My current xaml code looks like this:
<Border BorderBrush="{Binding ElementName=ImageViewerUserControl,
Path=NotificationColor}" BorderThickness="3" x:Name="AnimatedBorderBrush"
Visibility="{Binding ElementName=ImageViewerUserControl,
Path=ShowSequenceErrorNotification, Converter={StaticResource boolToVisibility}}">
<Border.Triggers>
<EventTrigger RoutedEvent="Border.Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="AnimatedBorderBrush"
Storyboard.TargetProperty="BorderBrush.Opacity"
RepeatBehavior="Forever"
AutoReverse="True"
From="1"
To="0.0"
Duration="0:0:1"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Border.Triggers>
</Border>
This only give the error:
Cannot resolve all property references in the property path 'BorderBrush.Opacity'. Verify that applicable objects support the properties.
But if I change to color of the BorderBrush to, lets say Black it works. How is this possible to achieve? I want to set the Brush color of my border via a dependency property. And yes, the dependency property is a Brush
I think the problem here is that the animation will only work if there is an object (the Brush) to animate. If you register your DependencyProperty without a default value it is null by default. Please try registering the DP with a default value
public static readonly DependencyProperty NotificationColorProperty = DependencyProperty.Register(
"NotificationColor",
typeof(Brush),
typeof(ImageViewerUserControl),
new PropertyMetadata(Brushes.Transparent)
);
Edit:
And as #Sheridan says use Storyboard.TargetProperty="Opacity" instead of Border.Opacity. Although it works if you specify a direct BorderBrush it doesn't worked for me with a bounded DP.
Your AnimatedBorderBrush name is misleading as it relates to a Border and not a BorderBrush. If you want to animate the Border.Opacity, then use Border.Opacity in the DoubleAnimation instead of BorderBrush.Opacity:
<DoubleAnimation Storyboard.TargetName="AnimatedBorderBrush"
Storyboard.TargetProperty="Border.Opacity"
RepeatBehavior="Forever"
AutoReverse="True"
From="1"
To="0.0"
Duration="0:0:1" />
UPDATE >>>
Ahhhhh, my bad... As the animation is defined inside the Border, there is no need to reference it, just use Opacity:
<DoubleAnimation Storyboard.TargetName="AnimatedBorderBrush"
Storyboard.TargetProperty="Opacity"
RepeatBehavior="Forever"
AutoReverse="True"
From="1"
To="0.0"
Duration="0:0:1" />

Binding and VisualStateManager do not work together

I have VisualStateManager to control when the State occurs, the control is enabled:
Here is the property of the state (string):
states:StateManager.VisualStateProperty="{Binding SomeProp}"
Here the VisualStateManager:
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="VisualStateGroup">
<VisualState x:Name="MyName">
<Storyboard>
<BooleanAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.IsEnabled)" Storyboard.TargetName="MyTextBox">
<DiscreteBooleanKeyFrame KeyTime="0" Value="True" />
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="HerName">
<Storyboard>
...
</Storyboard>
</VisualState>
<VisualState x:Name="This">
<Storyboard>
...
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
Here my text box:
<TextBox Name="MyTextBox" />
My question is: What happens when I add the TextBox the following line:
IsEnable= {Binding isProp}// isProp = bool
The way I see it, it eliminates the IsEnable of the TextBox and not refers to him, only to State.
Is this true? And is there a way they both work?
In your case, the Animation will take precedence over the binding, but only as long as the Animation's timeline is running. That is, when the visual state is "MyName", the animation will control the IsEnabled property; otherwise, the binding will.
You may be interested in this list of Dependency Property Value Precedence. The binding counts as a "Local value" and is of lower precedence than the animation.

EventTrigger on a TextBlock that sets a Border - Cannot resolve all property references

I have a Custom control in WPF in which I define a large ItemsControl Template.
In there, I have a Grid and in one column of that grid, I got a TextBlock and in another column I have a Border.
I want to highlight the Border when the mouse enters the TextBlock.
I tried several scenarios:
first an EventTrigger in the TextBlock's Style, but I learned that you can't do that, then an EventTrigger within the TextBlock's Triggers section, and now I just put it in the DataTemplate.Triggers of my ItemsControl, but I keep getting the error:
"Cannot resolve all property references in the property path 'Border.BorderBrush.Color'. Verify that applicable objects support the properties."
Here is the code that causes trouble:
<DataTemplate.Triggers>
<EventTrigger SourceName="mytxtblock" RoutedEvent="TextBlock.MouseEnter">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation Storyboard.TargetName="myborder"
Storyboard.TargetProperty="Border.BorderBrush.Color"
Duration="0:0:1"
To="White" />
<ThicknessAnimation Storyboard.TargetProperty="Border.BorderThickness"
Duration="0:0:1"
From="0"
To="1" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</DataTemplate.Triggers>
I think I'm missing something about the way i refer to the Color property of my Border, any insight?
Thanks!
EDIT: I figured out that declaring a SolidColorBrush in Resources and then using that value allows me to get rid of the
Storyboard.TargetProperty="Border.BorderBrush.Color" that changes to Storyboard.TargetProperty="Border.BorderBrush",
but now the compiler tells me that the color i declared (i tried Green and Transparent) is not a valid value for "To"...
Try
<ColorAnimation
Storyboard.TargetName="myborder"
Storyboard.TargetProperty="BorderBrush.(SolidColorBrush.Color)"
Duration="0:0:1"
To="White" />
but you have to declare a BorderBrush
BorderBrush="whatever"
or
<Border.BorderBrush>
<SolidColorBrush Color="whatever" />
</Border.BorderBrush>
in your "myborder" too.
On your ColorAnimation there are two properties:
Storyboard.TargetName="myborder"
Storyboard.TargetProperty="Border.BorderBrush.Color"
It implies, that myborder has a property called Border. I think that causes your error.

Animating "this" and "other" in WPF

Consider following DataTemplate in any List Control:
<DataTemplate>
<TextBlock Text="{Binding}" />
</DataTemplate>
and following animations:
<Window.Resources>
<Storyboard x:Key="animExpand">
<DoubleAnimation Storyboard.TargetProperty="Width" To="400" Duration="0:0:1" />
<DoubleAnimation Storyboard.TargetProperty="Height" To="400" Duration="0:0:1" />
</Storyboard>
<Storyboard x:Key="animCollapse">
<DoubleAnimation Storyboard.TargetProperty="Width" To="0" Duration="0:0:1" />
<DoubleAnimation Storyboard.TargetProperty="Height" To="0" Duration="0:0:1" />
</Storyboard>
</Window.Resources>
Now we want: when any of TextBlocks get clicked, "animExpand" apply to it and all other TextBlock have a "animCollapse".
First part is straightforward (a Trigger would do it) but the question is how to make other elements take part in this scenario?
I think I would put an boolean property (IsExpanded or something) in my model and then create a datatrigger to do the animations based on that value. When a mousedown occurs on a particular item, you'd have to write the logic to updates that boolean in the other objects in the list.

Resources