一点背景:
我正在构建一个窗口,我有一个故事板,用于在完成用户启动的任务时简要显示一个弹出窗口,而无需用户确认。它看起来像这样:
<UserControl.Resources>
<Storyboard x:Name="FadingFeedback" x:Key="FadingFeedback" Completed="FadingFeedback_Completed">
<DoubleAnimation
Storyboard.TargetProperty="Opacity"
From="0.5"
To="0"
BeginTime="0:0:0"
Duration="0:0:2.0">
<DoubleAnimation.EasingFunction>
<ExponentialEase Exponent="10" EasingMode="EaseIn" />
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard>
</UserControl.Resources>
我正在使用的弹出窗口定义如下:
<Popup Name="popup" Placement="Center" PopupAnimation="Fade" AllowsTransparency="True"
IsOpen="{Binding DoShowMessage}">
<Popup.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding DoShowMessage}" Value="true">
<Setter Property="Popup.IsOpen" Value="true" />
<DataTrigger.EnterActions>
<BeginStoryboard Storyboard="{StaticResource FadingFeedback}" />
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Popup.Style>
<Border Background="Black" BorderBrush="White" BorderThickness="1" CornerRadius="5">
<Grid Background="Transparent">
<Grid Name="popupGrid" Background="Black" Grid.Column="0" Grid.Row="0" />
<Label Name="popupLabel" HorizontalAlignment="Center" Foreground="White"
VerticalAlignment="Center"
Background="Transparent"
Grid.Column="0"
Grid.Row="0"
FontSize="16"
Content="{Binding TextMessage}"/>
</Grid>
</Border>
</Popup>
有一个ViewModel是这个的datacontext,它有几个属性(在Bindings中引用)来提供TextMessage字符串和DoShowMessage bool。它们的定义如下:
private string _textMessage;
public string TextMessage
{
get { return _textMessage; }
set
{
_textMessage = value;
OnPropertyChanged("TextMessage");
}
}
private bool _doShowMessage;
public bool DoShowMessage
{
get { return _doShowMessage; }
set
{
_doShowMessage = value;
OnPropertyChanged("DoShowMessage");
}
}
在适当的时候,ViewModel将设置DoShowMessage
属性,XAML中的绑定将显示弹出窗口并开始故事板。
现在,我在弹出窗口的代码后面有Completed
处理程序:
void StatusFader_Completed(object sender, EventArgs e)
{
popup.IsOpen = false;
}
并且运行良好,并且由于popup.IsOpen
绑定到ViewModel DoShowMessage
上的底层属性,它将重置该布尔值。
我的问题:
故事板完成后,是否有“更好”的方法来处理DoShowMessage属性的重置?或者换句话说,有没有办法在XAML中做到这一点?我已经阅读了意见/惯例,后面的代码应该(大部分)缺乏视图的代码,但似乎(至少对我来说)这(事件处理程序)是一种合理的方法。想知道是否有办法在XAML中做到这一点。
答案 0 :(得分:2)
是的,当然你可以用另一种方式来做。通过popup.IsOpen="False"
设置ObjectAnimationUsingKeyFrames
。
例如:
Storyboard
<Storyboard x:Name="FadingFeedback" x:Key="FadingFeedback">
<DoubleAnimation Storyboard.TargetProperty="Opacity" From="0.5"
To="0" BeginTime="0:0:0" Duration="0:0:2.0">
<DoubleAnimation.EasingFunction>
<ExponentialEase Exponent="10" EasingMode="EaseIn" />
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
<ObjectAnimationUsingKeyFrames BeginTime="0:0:2.0" Storyboard.TargetProperty="(Popup.IsOpen)">
<DiscreteObjectKeyFrame KeyTime="0:0:0">
<DiscreteObjectKeyFrame.Value>
<sys:Boolean>False</sys:Boolean>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
<Popup Name="popup" Placement="Center" PopupAnimation="Fade" AllowsTransparency="True" IsOpen="{Binding DoShowMessage}">
<Popup.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding DoShowMessage}" Value="True">
<!--<Setter Property="Popup.IsOpen" Value="True" />--> <!-- not necessarily, because we have Popup.IsOpen = True -->
<DataTrigger.EnterActions>
<BeginStoryboard Storyboard="{StaticResource FadingFeedback}" />
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Popup.Style>
...
</Popup>
但是在这种情况下,开场Popup
外部动画是有问题的,因此动画的属性是第一优先,但是其他来源,例如代码,指定不会。引自MSDN
:
为了产生任何实际效果,属性的动画必须能够优先于基础(非动画)值,即使该值是在本地设置的。
这样就可以通过代码访问,从属性中删除动画,从而允许访问属性,如下所示:
XAML
<Grid>
<Grid.Triggers>
<EventTrigger SourceName="CloseButton" RoutedEvent="Button.Click">
<BeginStoryboard>
<Storyboard>
<BooleanAnimationUsingKeyFrames Storyboard.TargetName="MyPopup"
Storyboard.TargetProperty="(Popup.IsOpen)"
Duration="0:0:0">
<DiscreteBooleanKeyFrame KeyTime="0:0:0" Value="False" />
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Grid.Triggers>
<Popup x:Name="MyPopup" Width="200" Height="200" IsOpen="True">
<Grid Background="Azure">
<Label Content="Test label" />
</Grid>
</Popup>
<Button Name="OpenButton" Content="OpenButtonFromCode" Width="140" Height="30" Click="OpenButton_Click" />
<Button Name="CloseButton" Content="CloseButtonfromEventTrigger" Width="180" Height="30" Margin="0,80,0,0" />
</Grid>
Code behind
private void OpenButton_Click(object sender, RoutedEventArgs e)
{
MyPopup.BeginAnimation(Popup.IsOpenProperty, null);
MyPopup.IsOpen = true;
}
可以选择,或者只是在事件popup.IsOpen
中放入 False 的Storyboard_Completed
,或者在动画中执行,但是要访问代码,它必须被删除。
对于我自己,我通常这样做,创建一个附加的依赖属性(boolean),将触发动画绑定到此属性。如果它的值为true,则启动动画,但是要重新运行,在事件Storyboard_Completed
中我必须将属性折叠为false。就个人而言,这是一种更简单的方法。