我有一个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;
}
}
}
答案 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;
}
}