消除故事板完成后面的代码处理程序

时间:2013-08-28 15:41:39

标签: c# wpf xaml mvvm

一点背景:

我正在构建一个窗口,我有一个故事板,用于在完成用户启动的任务时简要显示一个弹出窗口,而无需用户确认。它看起来像这样:

<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中做到这一点。

1 个答案:

答案 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。就个人而言,这是一种更简单的方法。