无法在WPF中暂停情节提要

时间:2019-02-06 19:06:35

标签: c# wpf

我有一个ListView,其中每个项目都包含两个在DataTemplate中定义的图像。当ListView的SelectedItem更改时,我使用样式触发器启动情节提要,以在4秒钟的总持续时间内更改SelectedItem的两个图像的不透明度。情节提要完成后,我将SelectedItem从代码更改为下一个项目,以便在下两个图像中出现相同的动画,依此类推。

我有一个暂停按钮,可以暂停情节提要,但它没有任何作用-情节提要动画仍在继续。我已经通过调试验证了是否在情节提要上调用了Pause按钮的click事件,并调用了Pause()方法。

这是我的UserControl:

<UserControl
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:ignore="http://www.galasoft.ch/ignore"
    xmlns:viewModel="clr-namespace:WpfTestBase.ViewModel"
    xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
    xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
    xmlns:local="clr-namespace:WpfTestBase"
    xmlns:Custom="http://www.galasoft.ch/mvvmlight"
    xmlns:sys="clr-namespace:System;assembly=mscorlib"
    x:Class="WpfTestBase.View.ListPresentationView"
    mc:Ignorable="d mc"
    d:DesignHeight="480"
    d:DesignWidth="640"
    >
<UserControl.Resources>
    <Storyboard x:Key="PromptStoryboard">
        <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)">
            <DiscreteDoubleKeyFrame KeyTime="0:0:0" Value="1">
            </DiscreteDoubleKeyFrame>
            <DiscreteDoubleKeyFrame KeyTime="0:0:4" Value="0.25"/>
        </DoubleAnimationUsingKeyFrames>
    </Storyboard>
    <Storyboard x:Key="TargetStoryboard">
        <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)">
            <DiscreteDoubleKeyFrame KeyTime="0:0:0" Value="1">
            </DiscreteDoubleKeyFrame>
            <DiscreteDoubleKeyFrame KeyTime="0:0:2" Value="0.25"/>
        </DoubleAnimationUsingKeyFrames>
    </Storyboard>
    <Storyboard x:Key="CombinedStoryboard" Completed="Storyboard_Completed_1">
        <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)">
            <DiscreteDoubleKeyFrame KeyTime="0:0:0" Value="1">
            </DiscreteDoubleKeyFrame>
            <DiscreteDoubleKeyFrame KeyTime="0:0:4" Value="0"/>
        </DoubleAnimationUsingKeyFrames>
    </Storyboard>

    <Style x:Key="CombinedListViewItemStyle" TargetType="{x:Type ListView}">
        <Style.Setters>
            <Setter Property="BorderThickness" Value="0" />
            <Setter Property="FontSize" Value="16" />
            <Setter Property="FontFamily" Value="Arial" />
            <Setter Property="ItemContainerStyle">
                <Setter.Value>
                    <Style TargetType="ListViewItem">
                        <Setter Property="Background" Value="Transparent" />
                        <Setter Property="Opacity" Value="1"/>
                        <Setter Property="HorizontalContentAlignment" Value="Stretch" />
                        <Setter Property="Template">
                            <Setter.Value>
                                <ControlTemplate TargetType="{x:Type ListViewItem}">
                                    <ContentPresenter />
                                </ControlTemplate>
                            </Setter.Value>
                        </Setter>
                        <Style.Triggers>
                            <Trigger Property="ListViewItem.IsSelected" Value="True">
                                <Trigger.EnterActions>
                                    <BeginStoryboard Storyboard="{StaticResource CombinedStoryboard}">
                                    </BeginStoryboard>
                                </Trigger.EnterActions>
                            </Trigger>
                        </Style.Triggers>
                    </Style>
                </Setter.Value>
            </Setter>
        </Style.Setters>
    </Style>

    <DataTemplate x:Key="CombinedDataTemplate">
        <Grid ShowGridLines="True">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="50"/>
                <ColumnDefinition Width="100"/>
                <ColumnDefinition Width="100"/>
            </Grid.ColumnDefinitions>

            <Image Grid.Column="1" Opacity="0.25" Source="{Binding PromptUriString}">
                <Image.Style>
                    <Style TargetType="{x:Type Image}">
                        <Style.Triggers>
                            <DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor,
                                                    AncestorType={x:Type ListBoxItem}}, Path=IsSelected}"
                                         Value="True">
                                <DataTrigger.EnterActions>
                                    <BeginStoryboard Storyboard="{StaticResource PromptStoryboard}">
                                    </BeginStoryboard>
                                </DataTrigger.EnterActions>
                            </DataTrigger>
                        </Style.Triggers>
                    </Style>
                </Image.Style>
            </Image>
            <Image Grid.Column="2" Opacity="0.25" Source="{Binding TargetUriString}">
                <Image.Style>
                    <Style TargetType="{x:Type Image}">
                        <Style.Triggers>
                            <DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor,
                                                    AncestorType={x:Type ListBoxItem}}, Path=IsSelected}"
                                         Value="True">
                                <DataTrigger.EnterActions>
                                    <BeginStoryboard Storyboard="{StaticResource TargetStoryboard}">
                                    </BeginStoryboard>
                                </DataTrigger.EnterActions>
                            </DataTrigger>
                        </Style.Triggers>
                    </Style>
                </Image.Style>
            </Image>

        </Grid>
    </DataTemplate>
</UserControl.Resources>

<i:Interaction.Triggers>
    <i:EventTrigger EventName="Loaded">
        <Custom:EventToCommand Command="{Binding LoadedCommand}"/>
    </i:EventTrigger>
</i:Interaction.Triggers>
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition/>
        <RowDefinition Height="50"/>
    </Grid.RowDefinitions>
    <Viewbox Stretch="Fill">
        <ListView x:Name="listViewCombined"
                  ItemsSource="{Binding Qs}" 
                  Margin="0" 
                  BorderBrush="{x:Null}"
                  BorderThickness="4"
                  SelectedItem="{Binding SelectedQ, Mode=TwoWay}"
                  SelectionChanged="listViewCombined_SelectionChanged"
                  ItemTemplate="{DynamicResource CombinedDataTemplate}"
                  Style="{StaticResource CombinedListViewItemStyle}"
                  IsHitTestVisible="False">
        </ListView>
    </Viewbox>
    <StackPanel Orientation="Horizontal"
                Grid.Row="1">
        <Button Name="PauseButton"
                Content="Pause"
                Click="Pause_Click"
                Margin="10"/>
        <Button Name="ResumeButton"
                Content="Resume"
                Click="Resume_Click"
                Margin="10"/>
    </StackPanel>
</Grid>
</UserControl>

还有我的代码背后:

using Microsoft.Win32;
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media.Animation;
using WpfTestBase.ViewModel;

namespace WpfTestBase.View
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class ListPresentationView
{
    public ListPresentationView()
    {
        InitializeComponent();
        DataContext = new ListPresentationViewModel();
    }

    private void listViewCombined_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        var sbPrompt = FindResource("PromptStoryboard") as Storyboard;
        listViewCombined.BeginStoryboard(sbPrompt);
    }

    private void Storyboard_Completed_1(object sender, EventArgs e)
    {
        var vm = this.DataContext as ListPresentationViewModel;
        vm.CombinedAnimationCompletedCommand.Execute(null);
    }

    private void SuspendOrResumeStoryboard(PowerModes mode)
    {
        if (mode == PowerModes.Resume || mode == PowerModes.Suspend)
        {
            var vm = this.DataContext as ListPresentationViewModel;
            if (vm != null)
            {
                try
                {
                    var sbPrompt = FindResource("PromptStoryboard") as Storyboard;
                    var sbTarget = FindResource("TargetStoryboard") as Storyboard;
                    var sbCombined = FindResource("CombinedStoryboard") as Storyboard;

                    if (sbPrompt != null && sbTarget != null)
                    {
                        if (mode == PowerModes.Suspend)
                        {
                            sbPrompt.Pause();
                            sbTarget.Pause();
                            sbCombined.Pause();

                            Console.WriteLine("===PAUSED" + " " + DateTime.Now.ToString("HH:mm:ss.fff"));
                        }
                        else if (mode == PowerModes.Resume)
                        {
                            sbPrompt.Resume();
                            sbTarget.Resume();
                            sbCombined.Resume();
                            Console.WriteLine("===RESUMED" + " " + DateTime.Now.ToString("HH:mm:ss.fff"));
                        }
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                }
            }
        }
    }

    private void Pause_Click(object sender, RoutedEventArgs e)
    {
        SuspendOrResumeStoryboard(PowerModes.Suspend);
    }

    private void Resume_Click(object sender, RoutedEventArgs e)
    {
        SuspendOrResumeStoryboard(PowerModes.Resume);
    }
}
}

还有我的ViewModel:

using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using System;
using System.Collections.Generic;
using WpfTestBase.Model;

namespace WpfTestBase.ViewModel
{
/// <summary>
/// This class contains properties that a View can data bind to.
/// <para>
/// See http://www.galasoft.ch/mvvm
/// </para>
/// </summary>
public class ListPresentationViewModel : ViewModelBase
{
    private int _currCombinedIndex = 0;
    public List<WmtQ> Qs { get; set; }

    /// <summary>
    /// The <see cref="SelectedQ" /> property's name.
    /// </summary>
    public const string SelectedQPropertyName = "SelectedQ";

    private WmtQ _selectedQ = null;

    /// <summary>
    /// Sets and gets the SelectedQ property.
    /// Changes to that property's value raise the PropertyChanged event. 
    /// </summary>
    public WmtQ SelectedQ
    {
        get
        {
            return _selectedQ;
        }
        set
        {
            Set(() => SelectedQ, ref _selectedQ, value);

        }
    }

    private RelayCommand _loadedCommand;

    /// <summary>
    /// Gets the LoadedCommand.
    /// </summary>
    public RelayCommand LoadedCommand
    {
        get
        {
            return _loadedCommand
                ?? (_loadedCommand = new RelayCommand(
                () =>
                {
                    SelectedQ = Qs[_currCombinedIndex];
                }));
        }
    }

    private RelayCommand _combinedAnimationCompletedCommand;

    /// <summary>
    /// Gets the CombinedAnimationCompletedCommand.
    /// </summary>
    public RelayCommand CombinedAnimationCompletedCommand
    {
        get
        {
            return _combinedAnimationCompletedCommand
                ?? (_combinedAnimationCompletedCommand = new RelayCommand(
                () =>
                {
                    _currCombinedIndex++;
                    if (_currCombinedIndex < Qs.Count)
                    {
                        SelectedQ = Qs[_currCombinedIndex];
                    }
                }));
        }
    }

    /// <summary>
    /// Initializes a new instance of the ListPresentationViewModel class.
    /// </summary>
    public ListPresentationViewModel()
    {
        Qs = new List<WmtQ>();
        var qList = new List<WmtQ> { new WmtQ("One", "A"), new WmtQ("Two", "B"), new WmtQ("Three", "C"), new WmtQ("Four", "D") };
        Qs = qList;
    }

}
}

2 个答案:

答案 0 :(得分:1)

根据documentation,看来BeginStoryboard标签需要有一个名称才能使其可控制:

  

如果通过指定其名称为BeginStoryboard命名   属性,您可以将其设为可控制的情节提要。那你可以   启动情节提要后,以交互式方式控制情节提要。

但是,我还读到some people(请参见Christine L.的答案)在混合XAML触发器和隐藏代码时,故事板存在问题。如果您无法使其正常工作,则最好只使用触发器,也可以只使用代码隐藏,但不要同时使用两者。

如果您要使用后台代码,则需要通过在{{上调用true方法时为IsControllable参数指定Begin来控制情节提要1}}。

答案 1 :(得分:0)

感谢redcurry的回答和mm8的评论,我通过将情节提要板完全移到代码隐藏位置来解决了这个问题。

我的用户控件:

<UserControl.Resources>
    <Style x:Key="CombinedListViewItemStyle" TargetType="{x:Type ListView}">
        <Style.Setters>
            <Setter Property="BorderThickness" Value="0" />
            <Setter Property="FontSize" Value="16" />
            <Setter Property="FontFamily" Value="Arial" />
            <Setter Property="ItemContainerStyle">
                <Setter.Value>
                    <Style TargetType="ListViewItem">
                        <Setter Property="Background" Value="Transparent" />
                        <Setter Property="Opacity" Value="1"/>
                        <Setter Property="HorizontalContentAlignment" Value="Stretch" />
                        <Setter Property="Template">
                            <Setter.Value>
                                <ControlTemplate TargetType="{x:Type ListViewItem}">
                                    <ContentPresenter />
                                </ControlTemplate>
                            </Setter.Value>
                        </Setter>
                    </Style>
                </Setter.Value>
            </Setter>
        </Style.Setters>
    </Style>

    <DataTemplate x:Key="CombinedDataTemplate">
        <Grid ShowGridLines="True">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="50"/>
                <ColumnDefinition Width="100"/>
                <ColumnDefinition Width="100"/>
            </Grid.ColumnDefinitions>
            <Image Grid.Column="1" Opacity="0.25" Source="{Binding PromptUriString}"
                   Name="PromptImage">
            </Image>
            <Image Grid.Column="2" Opacity="0.25" Source="{Binding TargetUriString}"
                   Name="TargetImage">
            </Image>
        </Grid>
    </DataTemplate>
</UserControl.Resources>

<i:Interaction.Triggers>
    <i:EventTrigger EventName="Loaded">
        <Custom:EventToCommand Command="{Binding LoadedCommand}"/>
    </i:EventTrigger>
</i:Interaction.Triggers>
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition/>
        <RowDefinition Height="50"/>
    </Grid.RowDefinitions>
    <Viewbox Stretch="Fill">
        <ListView x:Name="listViewCombined"
                  ItemsSource="{Binding Qs}" 
                  Margin="0" 
                  BorderBrush="{x:Null}"
                  BorderThickness="4"
                  SelectedItem="{Binding SelectedQ, Mode=TwoWay}"
                  SelectionChanged="listViewCombined_SelectionChanged"
                  ItemTemplate="{DynamicResource CombinedDataTemplate}"
                  Style="{StaticResource CombinedListViewItemStyle}"
                  IsHitTestVisible="False"
                  IsSynchronizedWithCurrentItem="True">
        </ListView>
    </Viewbox>
    <StackPanel Orientation="Horizontal"
                Grid.Row="1">
        <Button Name="PauseButton"
                Content="Pause"
                Click="Pause_Click"
                Margin="10"/>
        <Button Name="ResumeButton"
                Content="Resume"
                Click="Resume_Click"
                Margin="10"/>
    </StackPanel>
</Grid>

隐藏代码:

public partial class ListPresentationView
{
    Storyboard sbPrompt;
    Storyboard sbTarget;

    bool _isCompSleep = false;

    List<int> _completedListViewIndices = new List<int>();

    public ListPresentationView()
    {
        InitializeComponent();
        DataContext = new ListPresentationViewModel();

        // Needed for controlling storyboards
        NameScope.SetNameScope(this, new NameScope());
    }

    private void UserControl_Loaded(object sender, RoutedEventArgs e)
    {
        RegisterElementsInNameScope();
        AnimateCurrentItem(0);
    }

    private void RegisterElementsInNameScope()
    {
        var gen = listViewCombined.ItemContainerGenerator;
        var obj = (gen.ContainerFromItem(listViewCombined.Items[0]));
        if (obj != null)
        {
            ListViewItem myListBoxItem = (ListViewItem)obj;

            ContentPresenter myContentPresenter = FindVisualChild<ContentPresenter>(myListBoxItem);

            DataTemplate myDataTemplate = myContentPresenter.ContentTemplate;
            Image promptImage = (Image)myDataTemplate.FindName("PromptImage", myContentPresenter);
            Image targetImage = (Image)myDataTemplate.FindName("TargetImage", myContentPresenter);

            this.RegisterName(listViewCombined.Name, listViewCombined);
            this.RegisterName(promptImage.Name, promptImage);
            this.RegisterName(targetImage.Name, targetImage);
        }
    }

    private void AnimateCurrentItem(int currIndex)
    {
        Console.WriteLine("AnimateCurreintItem, currIndex: " + currIndex);
        var gen = listViewCombined.ItemContainerGenerator;
        var obj = (gen.ContainerFromItem(listViewCombined.Items[currIndex]));
        if (obj != null)
        {
            ListViewItem myListBoxItem = (ListViewItem)obj;

            ContentPresenter myContentPresenter = FindVisualChild<ContentPresenter>(myListBoxItem);

            DataTemplate myDataTemplate = myContentPresenter.ContentTemplate;
            Image promptImage = (Image)myDataTemplate.FindName("PromptImage", myContentPresenter);
            Image targetImage = (Image)myDataTemplate.FindName("TargetImage", myContentPresenter);

            this.UnregisterName(listViewCombined.Name);
            this.UnregisterName(promptImage.Name);
            this.UnregisterName(targetImage.Name);
            this.RegisterName(listViewCombined.Name, listViewCombined);
            this.RegisterName(promptImage.Name, promptImage);
            this.RegisterName(targetImage.Name, targetImage);

            DoubleAnimation promptAni = new DoubleAnimation();
            promptAni.From = 1;
            promptAni.To = 0;
            promptAni.Duration = new Duration(TimeSpan.FromMilliseconds(4000));

            sbPrompt = new Storyboard();
            sbPrompt.Children.Add(promptAni);
            Storyboard.SetTargetName(promptAni, promptImage.Name);
            Storyboard.SetTargetProperty(promptAni, new PropertyPath(Image.OpacityProperty));

            DoubleAnimationUsingKeyFrames targetAni = new DoubleAnimationUsingKeyFrames();
            var kf1 = new DiscreteDoubleKeyFrame(0.25, KeyTime.FromTimeSpan(TimeSpan.FromSeconds(0)));
            var kf2 = new DiscreteDoubleKeyFrame(1, KeyTime.FromTimeSpan(TimeSpan.FromSeconds(2)));
            var kf3 = new DiscreteDoubleKeyFrame(0.25, KeyTime.FromTimeSpan(TimeSpan.FromSeconds(4)));
            targetAni.KeyFrames.Add(kf1);
            targetAni.KeyFrames.Add(kf2);
            targetAni.KeyFrames.Add(kf3);

            if (sbTarget != null) sbTarget.Completed -= Storyboard_Completed_1;
            sbTarget = new Storyboard();
            sbTarget.Completed += Storyboard_Completed_1;

            sbTarget.Children.Add(targetAni);
            Storyboard.SetTargetName(targetAni, targetImage.Name);
            Storyboard.SetTargetProperty(targetAni, new PropertyPath(Image.OpacityProperty));

            sbPrompt.Begin(this, true);
            sbTarget.Begin(this, true);

        }
        else
        {
            ;
        }
    }

    private void listViewCombined_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        var lView = sender as ListView;
        if (lView != null)
        {
            var index = lView.SelectedIndex;
            if (index >= 0 && index < lView.Items.Count)
            {
                AnimateCurrentItem(index);
            }
        }
    }

    private childItem FindVisualChild<childItem>(DependencyObject obj)
        where childItem : DependencyObject
    {
        for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
        {
            DependencyObject child = VisualTreeHelper.GetChild(obj, i);
            if (child != null && child is childItem)
                return (childItem)child;
            else
            {
                childItem childOfChild = FindVisualChild<childItem>(child);
                if (childOfChild != null)
                    return childOfChild;
            }
        }
        return null;
    }

    private void Storyboard_Completed_1(object sender, EventArgs e)
    {
        Console.WriteLine("Storyboard_Completed_1 " + DateTime.Now.ToString("HH:mm:ss.fff"));
        var vm = this.DataContext as ListPresentationViewModel;

        if (!_isCompSleep) vm.CombinedAnimationCompletedCommand.Execute(null);
    }

    private void SuspendOrResumeStoryboard(PowerModes mode)
    {
        if (mode == PowerModes.Resume || mode == PowerModes.Suspend)
        {
            {
                try
                {
                    if (sbPrompt != null && sbTarget != null)
                    {
                        if (mode == PowerModes.Suspend)
                        {
                            _isCompSleep = true;
                            sbPrompt.Pause(this);
                            sbTarget.Pause(this);
                            Console.WriteLine("===PAUSED" + " " + DateTime.Now.ToString("HH:mm:ss.fff"));
                        }
                        else if (mode == PowerModes.Resume)
                        {
                            _isCompSleep = false;
                            sbPrompt.Resume(this);
                            sbTarget.Resume(this);
                            Console.WriteLine("===RESUMED" + " " + DateTime.Now.ToString("HH:mm:ss.fff"));
                        }
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                }
            }
        }
    }

    private void Pause_Click(object sender, RoutedEventArgs e)
    {
        SuspendOrResumeStoryboard(PowerModes.Suspend);
    }

    private void Resume_Click(object sender, RoutedEventArgs e)
    {
        SuspendOrResumeStoryboard(PowerModes.Resume);
    }
}

和视图模型:

public class ListPresentationViewModel : ViewModelBase
{
    private int _currCombinedIndex = 0;
    public List<WmtQ> Qs { get; set; }

    /// <summary>
    /// The <see cref="SelectedQ" /> property's name.
    /// </summary>
    public const string SelectedQPropertyName = "SelectedQ";

    private WmtQ _selectedQ = null;

    /// <summary>
    /// Sets and gets the SelectedQ property.
    /// Changes to that property's value raise the PropertyChanged event. 
    /// </summary>
    public WmtQ SelectedQ
    {
        get
        {
            return _selectedQ;
        }
        set
        {
            Set(() => SelectedQ, ref _selectedQ, value);

        }
    }

    private RelayCommand _loadedCommand;

    /// <summary>
    /// Gets the LoadedCommand.
    /// </summary>
    public RelayCommand LoadedCommand
    {
        get
        {
            return _loadedCommand
                ?? (_loadedCommand = new RelayCommand(
                () =>
                {
                    SelectedQ = Qs[_currCombinedIndex];
                }));
        }
    }

    private RelayCommand _combinedAnimationCompletedCommand;

    /// <summary>
    /// Gets the CombinedAnimationCompletedCommand.
    /// </summary>
    public RelayCommand CombinedAnimationCompletedCommand
    {
        get
        {
            return _combinedAnimationCompletedCommand
                ?? (_combinedAnimationCompletedCommand = new RelayCommand(
                () =>
                {
                    _currCombinedIndex++;
                    if (_currCombinedIndex < Qs.Count)
                    {
                        SelectedQ = Qs[_currCombinedIndex];
                    }
                }));
        }
    }

    /// <summary>
    /// Initializes a new instance of the ListPresentationViewModel class.
    /// </summary>
    public ListPresentationViewModel()
    {
        Qs = new List<WmtQ>();
        var qList = new List<WmtQ> { new WmtQ("One", "A"), new WmtQ("Two", "B"), new WmtQ("Three", "C"), new WmtQ("Four", "D") };
        Qs = qList;
    }

}