如何在ViewModel中播放Storyboard?

时间:2013-07-21 06:56:43

标签: wpf mvvm

我在View

中定义了一个故事板
  <Storyboard x:Key="ExpandAdd" >
            <ObjectAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="AddUsers" Storyboard.TargetProperty="(UIElement.Visibility)">
                <DiscreteObjectKeyFrame KeyTime="00:00:00" Value="{x:Static Visibility.Collapsed}"/>
                <DiscreteObjectKeyFrame KeyTime="00:00:00.3000000" Value="{x:Static Visibility.Visible}"/>
            </ObjectAnimationUsingKeyFrames>
            <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="DetailBorder" Storyboard.TargetProperty="(UIElement.Opacity)">
                <SplineDoubleKeyFrame KeyTime="00:00:00" Value="1"/>
                <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="0.4"/>
            </DoubleAnimationUsingKeyFrames>
            <BooleanAnimationUsingKeyFrames BeginTime="00:00:00" Duration="00:00:00.0010000" Storyboard.TargetName="DetailBorder" Storyboard.TargetProperty="(UIElement.IsEnabled)">
                <DiscreteBooleanKeyFrame KeyTime="00:00:00" Value="False"/>
            </BooleanAnimationUsingKeyFrames>
        </Storyboard>

我有一个按钮并绑定到relaycommand。

<Button x:Name="AddUserButton"  Content="اضافه">
   <i:Interaction.Triggers>
      <i:EventTrigger EventName="Click">
        <cmd:EventToCommand Command="{Binding AddUsers}"/>
      </i:EventTrigger>
   </i:Interaction.Triggers>
</Button>

我想在执行RelayCommand(添加用户)时播放故事板。

5 个答案:

答案 0 :(得分:4)

您不应该从Storyboard访问ViewModel。它完全违背了MVVM的目的。

你可以在Button.LostMouseCapture事件上应用故事板,在Click事件上调用命令后引发该事件 -

<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>

答案 1 :(得分:2)

实际上,通过使用绑定到ViewModel中的属性的DataTrigger可以相对容易地做到这一点,从而从MV中触发Storyboard,同时仍然保持关注点的分离。这是一个非常简短的示例,其中Storyboard根据VM中的bool变为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>

答案 2 :(得分:0)

请不要从viewmodel触发故事板。如果你这样做,那就没有观点模型。

在视图后面的代码中实现relay命令的任何代码,然后如果需要,可以调用viewmodel来做任何事情 这是viewmodel特定的。当您从视图后面的代码引用故事板时,您可以按名称引用它。

答案 3 :(得分:0)

只要您将其分配给x:Name,就可以从代码隐藏中调用Storyboard。但是,您需要实现一个button_click事件处理程序,而不是ViewModel的命令。

如果您正在使用MVVM,那么您应该遵守ViewModel不应该“了解”View的具体细节的原则。

答案 4 :(得分:0)

对于UWP :您可以在VisualStates中定义故事板,然后通过更改ViewModel中的属性来运行它。如果您将IsRefreshWorking属性更改为true动画开始。

使用IsTrueTrigger定义VisualState:

<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>
             <base:IsTrueTrigger  PropertyToCompare="{x:Bind ViewModel.IsRefreshWorking, Mode=OneWay}" />
         </VisualState.StateTriggers>
      </VisualState>
   </VisualStateGroup>
</VisualStateManager.VisualStateGroups>

带动画的AppBarButton:

<AppBarButton>
  <AppBarButton.Icon>
     <SymbolIcon x:Name="RefreshIcon"  Symbol="Refresh" RenderTransformOrigin="0.5,0.5" >
         <SymbolIcon.RenderTransform>
            <CompositeTransform/>
         </SymbolIcon.RenderTransform>
      </SymbolIcon>
  </AppBarButton.Icon>
</AppBarButton>

IsTrueTrigger的完整性:

public class IsTrueTrigger : StateTriggerBase
{
  public bool PropertyToCompare
    {
        get => (bool)GetValue(PropertyToCompareProperty);
        set => SetValue(PropertyToCompareProperty, value);
    }

    public static readonly DependencyProperty PropertyToCompareProperty =
        DependencyProperty.Register("PropertyToCompare", typeof(bool), typeof(IsTrueTrigger),
            new PropertyMetadata(null, OnValuePropertyChanged));


    public bool IsInverse
    {
        get => (bool)GetValue(IsInverseProperty);
        set => SetValue(IsInverseProperty, value);
    }

    public static readonly DependencyProperty IsInverseProperty =
        DependencyProperty.Register("IsInverseProperty", typeof(bool), typeof(IsTrueTrigger),
            new PropertyMetadata(null, OnValuePropertyChanged));

    private static void OnValuePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var obj = (IsTrueTrigger)d;
        obj.UpdateTrigger();
    }
  private void UpdateTrigger()
   {
      SetActive(IsInverse ? !PropertyToCompare : PropertyToCompare);
   }
}