I defiend a storyborad in View
You could call the Storyboard from a code-behind as long as you assigned it x:Name. But then, you'd need to implement a button_click event handler, not a command for the ViewModel.
If you're using MVVM then you should really abide by the principle that the ViewModel should not "know" about specifics of the View.
You should not access Storyboard
from your ViewModel
. It defeats the purpose of MVVM
altogether.
You can apply storyboard on Button.LostMouseCapture
event which gets raised after your command gets called on Click
event -
<Button x:Name="AddUserButton" Content="اضافه">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<cmd:EventToCommand Command="{Binding AddUsers}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<Button.Triggers>
<EventTrigger RoutedEvent="Button.LostMouseCapture">
<BeginStoryboard Storyboard="{StaticResource ExpandAdd}">
</EventTrigger>
</Button.Triggers>
</Button>
For UWP: You can define your storyboard in VisualStates
and run it by changing property in ViewModel. If you change IsRefreshWorking
property to true
animation begins.
Define VisualState, using IsTrueTrigger:
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="RefreshProgressVisualStates">
<VisualState x:Name="NotWorkingVisualState" />
<VisualState x:Name="WorkingVisualState">
<Storyboard AutoReverse="False" RepeatBehavior="Forever">
<DoubleAnimation Duration="0:0:1" To="360" Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.Rotation)" Storyboard.TargetName="RefreshIcon" />
</Storyboard>
<VisualState.StateTriggers>
<StateTrigger IsActive="{x:Bind ViewModel.IsRefreshWorking, Mode=OneWay}" />
</VisualState.StateTriggers>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
AppBarButton with animation:
<AppBarButton>
<AppBarButton.Icon>
<SymbolIcon x:Name="RefreshIcon" Symbol="Refresh" RenderTransformOrigin="0.5,0.5" >
<SymbolIcon.RenderTransform>
<CompositeTransform/>
</SymbolIcon.RenderTransform>
</SymbolIcon>
</AppBarButton.Icon>
</AppBarButton>
Please don't trigger the storyboard from the viewmodel. If you do then there is no point having a viewmodel.
Implement any code for the relay command in the code behind of the view, then if needed you can call through to the viewmodel to do anything that is viewmodel specific. When you reference the storyboard from the code behind of the view you can just refer to it by name.
Actually, this is relatively easy to do by using a DataTrigger that is bound to a property in the ViewModel, thus triggering the Storyboard from the MV while still maintaining separation of concerns. Here's a very brief example where a Storyboard grows an image in size in response to a bool in the VM changing to True.
<Image
Grid.Row="0"
x:Name="LockImage"
Source="/StoryboardManagerExample;component/Resources/LockedPadlock.png"
Width="50"
RenderTransformOrigin="0.5,0.5">
<Image.RenderTransform>
<TransformGroup>
<ScaleTransform x:Name="ptScale" ScaleX="1" ScaleY="1"/>
<SkewTransform />
<RotateTransform />
<TranslateTransform />
</TransformGroup>
</Image.RenderTransform>
<Image.Style>
<Style>
<Style.Triggers>
<DataTrigger
Binding="{Binding RunStoryboard}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetProperty="Width"
From="50"
To="100"
Duration="0:0:1"
RepeatBehavior="1x"
AutoReverse="True"
/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>