我在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(添加用户)时播放故事板。
答案 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);
}
}