Invoking a Viewmodel Method From an Event Handler In a Resource Dictionary - wpf

What I'm trying to do is wire up the Completed event of a StoryBoard that's declared in a Resource Dictionary to invoke a method on my viewmodel.
I declared the StoryBoard in a Resource Dictionary because it's used by 5 Styles, also declared in this Resource Dictionary. My Resource Dictionary is declared as below
<ResourceDictionary x:Class="PrimaryAssets"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:pry="clr-namespace:PowderKegSoftware.Artisan.Client.Primary"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity">
<BeginStoryboard x:Key="ShowAspect">
<Storyboard TargetProperty="Opacity"
FillBehavior="HoldEnd"
Completed="HandleTransitionCompleted">
<DoubleAnimation To="1"
Duration="0:0:0.4" />
</Storyboard>
</BeginStoryboard>
.............
I added a method to my views code behind called HandleTransitionCompleted which invoked my viewmodel's method but I can't get a reference to my view from this Resource Dictionary.
I tried using a command by making HandelTransitionCompleted a property that holds an ICommand instance and using a command in my StoryBoard like so
<BeginStoryboard x:Key="ShowAspect">
<Storyboard TargetProperty="Opacity"
FillBehavior="HoldEnd">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Completed">
<i:InvokeCommandAction
Command="{Binding Path=HandelTransitionCompleted,
Mode=OneWay}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<DoubleAnimation To="1"
Duration="0:0:0.4" />
</Storyboard>
</BeginStoryboard>
.........
This throws an exception saying "Cannot freeze this Storyboard timeline ....." and after doing some reading I understand why.
Is there a way to get this event handler to invoke the method on my viewmodel.
Any help would be most welcome and thanks in advance.

Related

How to reuse a single literal value to initialize multiple different types in a XAML resource dictionary?

Many types in XAML have converters that accept identical format inputs, but the types themselves are not implicitly compatible. For instance "0:0:4" can be a Duration or a Keytime among many others. And sometimes I'd like to use the same value for these sort of things. For instance perhaps I have a KeyTime that starts right after the Duration of another animation:
<Duration x:Key="Foo">0:0:4</Duration>
<KeyTime x:Key="Bar">0:0:4</KeyTime>
The problem with this is that there's no way to express the contract that these values should be the same. So what I'd like is something along the lines of:
<system:String x:Key="AnimationTime">0:0:4</system:String>
<Duration x:Key="Foo">[AnimationTime]</Duration>
<KeyTime x:Key="Bar">[AnimationTime]</KeyTime>
But I'm uncertain of the syntax for this. I assume I need to invoke the converters for the Duration/Keytime but I'm finding XAML syntax is not always the most intuitive.
Instead of directly using StaticResource like
KeyTime="{StaticResource AnimationTime}"
you may often use a Binding with the resource as Source object like
KeyTime="{Binding Source={StaticResource AnimationTime}}"
and thus benefit from automatic type conversion.
An example:
<Window.Resources>
<system:String x:Key="AnimationTime">0:0:4</system:String>
<Storyboard x:Key="ExampleStoryboard">
<DoubleAnimation To="1"
Storyboard.TargetProperty="Opacity"
Duration="{Binding Source={StaticResource AnimationTime}}"/>
<ObjectAnimationUsingKeyFrames
Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame
KeyTime="{Binding Source={StaticResource AnimationTime}}"
Value="{x:Static Brushes.Green}"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</Window.Resources>
<Grid Background="Red" Opacity="0">
<Grid.Triggers>
<EventTrigger RoutedEvent="Loaded">
<BeginStoryboard
Storyboard="{StaticResource ExampleStoryboard}"/>
</EventTrigger>
</Grid.Triggers>
</Grid>

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,

Storyboard To value staticresource

Can figure this one out, I have some staticresources in aaplication.xaml
These staticresources i use in differtent places, per design. Now i want to use a Staticresource to a Coloranimation in a Storyboard but i can't get it to work i get the error:
An object of the type "System.Windows.Media.SolidColorBrush" cannot be applied to a property that expects the type "System.Nullable1[[System.Windows.Media.Color,....]]
Code so far:
Application.XAML
<Application.Resources>
<SolidColorBrush x:Key="GreenLight" Color="#0CAF12" />
</Application.Resources>
In a usercontrol label style:
<Setter Property="Label.Content" Value="Connected" />
<DataTrigger.EnterActions>
<BeginStoryboard Name="StoryConnected">
<Storyboard Storyboard.TargetProperty="(Background).(SolidColorBrush.Color)">
<ColorAnimation Storyboard.TargetProperty="(Background).(SolidColorBrush.Color)" To="{StaticResource GreenLight}" Duration="0:0:0.5" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<RemoveStoryboard BeginStoryboardName="StoryConnected" />
</DataTrigger.ExitActions>
This won't work because the Storyboard cannot be frozen when you bind the To property:
To="{Binding Color, Source={StaticResource GreenLight}}"
So you actually need to set the To property to a Color object, i.e. define your resource like this:
<Color x:Key="GreenLight">#0CAF12</Color>

WPF ColorAnimation inside Window.Resources

I have a TextBlock that I would like to give it a color animation effect; I have done something like this:
<Window.Resources>
<Storyboard x:Key="AnimateTarget" RepeatBehavior="Forever">
<ColorAnimation AutoReverse="False" Duration="0:0:5" From="Red" To="black" Storyboard.TargetName="txtBarcode" AccelerationRatio="1" Storyboard.TargetProperty="(TextBlock.Background).(SolidColorBrush.Color)" FillBehavior="HoldEnd">
</ColorAnimation>
</Storyboard>
</Window.Resources>
I start the animation from code behind:
((Storyboard)this.Resources["AnimateTarget"]).Begin();
but when i start the animation its give me the following error:
'Background' property does not point to a DependencyObject in path '(0).(1)'.
I would priciest if someone helps me on this,
Thanks,
Give your TextBlock any Background and it will work.
<TextBlock Name="txtBarcode"
Background="Transparent"
Probably Background is Null and so there is no DependencyObject to animate.

Binding a Storyboard property relative to the target of the storyboard

I have a storyboard which targets an element, and binds one of its own properties to a property on another element:
<Storyboard>
<DoubleAnimation
Storyboard.TargetProperty="RenderTransform.X"
From="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}, Path=ActualWidth}"
To="0"
Duration="0:0:5"/>
</Storyboard>
This storyboard works when the storyboard is stored in the resources of the window which holds the storyboard target. The 'From' value is correctly bound to the ActualWidth of the host Window instance.
However, I need to store the storyboard in my application level resources. From here, the storyboard does not seem to be able to target the window to determine the 'From' property. This is understandable as from inside <Application.Resources>, the binding won't be able to find an 'ancestor' of type Window.
I guess I need to be able to bind the 'From' value, relative to the target of the animation, rather than relative to the storyboard's DoubleAnimation.
Is this possible, and if so, how?
Here is the sample MainWindow.xaml:
<Window.Resources>
<!--This works : Storyboard correctly sets 'From' property to 'ActualWidth' of window-->
<Storyboard x:Key="localStoryBoard">
<DoubleAnimation
Storyboard.TargetProperty="RenderTransform.X"
From="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}, Path=ActualWidth}"
To="0"
Duration="0:0:5"/>
</Storyboard>
</Window.Resources>
<StackPanel>
<Button
RenderTransformOrigin="0,1"
HorizontalAlignment="Left"
Content="Click me">
<Button.RenderTransform>
<TranslateTransform/>
</Button.RenderTransform>
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<EventTrigger.Actions>
<BeginStoryboard Storyboard="{StaticResource centralStoryBoard}"/>
</EventTrigger.Actions>
</EventTrigger>
</Button.Triggers>
</Button>
</StackPanel>
And here is an example app.xaml:
<Application x:Class="WpfApplication3.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml">
<Application.Resources>
<!--Storyboard doesn't work at all-->
<Storyboard x:Key="centralStoryBoard">
<DoubleAnimation
Storyboard.TargetProperty="RenderTransform.X"
From="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}, Path=ActualWidth}"
To="0"
Duration="0:0:5"/>
</Storyboard>
</Application.Resources>
</Application>
This won't work, as the eventtrigger refers to the app.xaml version. If you change it to the local resource version, you can see it works.
The example doesn't work because when you put your storyboard to resources it has no Window ancestor. What actually RelativeSource do is search element tree backwards waiting for the node with AncestorType appear and then binds it.
When put in the Window.Resources there's actual Window back in the tree and it binds it correctly. When put to the application resources there's no Window back in the tree because it's not connected to the Window whatsoever.
If you really want to put your storyboard to the application resources you should consider giving up the idea with binding. Instead, you can check out this answer with code for a clipper, I think this is what you need - https://stackoverflow.com/a/59376318/11178539.

Resources