【发布时间】:2021-08-14 04:34:00
【问题描述】:
我有一个具有以下边框定义的自定义控件:
<Border Width="10" Height="10"
CornerRadius="5"
Background="Red"
BorderBrush="White"
BorderThickness="1">
<Border.Style>
<Style TargetType="Border">
<Setter Property="Opacity" Value="0.0"/>
<Style.Triggers>
<DataTrigger Binding="{Binding MyState}" Value="{x:Static my:MyStates.Initializing}">
<DataTrigger.EnterActions>
<StopStoryboard BeginStoryboardName="Animate"/>
<BeginStoryboard x:Name="Animate" HandoffBehavior="SnapshotAndReplace">
<Storyboard Duration="0:0:0.4">
<DoubleAnimation AutoReverse="True" Storyboard.TargetProperty="Opacity" To="1.0" Duration="0:0:0.2" FillBehavior="Stop" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
</Border>
意图是:当MyState变为MyStates.Initializing的值时,边框应该再次淡入淡出。
我没有定义<DataTrigger.ExitActions>,因为MyState很快又会被改变;当MyState 设置为不同的值时,什么都不会发生(动画结束除外)。
奇怪的是:这只工作一次。如果我有两个相同控件的实例:两者都会触发一次,但不会再触发一次。如果我再添加另一个实例,它也不会触发。
我搜索了无数的链接和建议,例如:
WPF Animation Only Firing Once
Animation inside DataTrigger won't run a second time
DataTrigger and Storyboard only getting executed once, related to order of declaration?
我错过了什么?
编辑:
为了澄清(在阅读下面的评论后),动画在第一次被触发时有效,但之后再也没有。如果MyState 更改为Initializing,则(显然)正确触发。然后(在动画完成之前,几毫秒或更短的时间之后)MyState 更改为 Whatever 并且动画根据需要完成。如果MyState then 再次更改为Initializing(稍后,在动画完成很久之后),什么也没有发生。
另外:如果我有两个实例响应它们各自的 MyState 更改为 Initializing(在几毫秒或更短的时间内),两者都会正确触发。然后我可以添加一个全新的实例,并且不会被触发。
我还检查了第二个实例是否会正确触发,如果触发器(设置MyState == Initialized)在第一个实例的动画完成之后出现。是的,触发器第一次触发时存在的每个实例都将被正确触发一次。之后:什么都没有……
编辑:
以下是我为隔离和测试问题而编写的代码。在之前的编辑中,我假设代码没问题,我的错误必须隐藏在其他地方,但我测试了 1000 毫秒的睡眠持续时间(见下面的代码)。如果我将其减少到 10 毫秒,我的问题仍然存在。因此,这似乎是一个数据绑定问题,而不是动画问题。
AnimationTestControl.xaml.cs:
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace AnimationTest
{
public enum MyStates
{
None = 0,
Initializing = 1,
Ready = 2,
}
public class TestItem : INotifyPropertyChanged
{
private MyStates _myState;
public MyStates MyState
{
get => _myState;
set { _myState = value; OnPropertyChanged(); }
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public partial class AnimationTestControl : UserControl
{
public ObservableCollection<TestItem> TestItems { get; } = new ObservableCollection<TestItem>();
public AnimationTestControl()
{
InitializeComponent();
TestItems.Add(new TestItem());
}
private void ButtonStart_Click(object sender, RoutedEventArgs e)
{
Task.Run(() =>
{
foreach (var testItem in TestItems)
{
testItem.MyState = MyStates.Initializing;
Thread.Sleep(10); //1000ms = good, 10ms = bad
testItem.MyState = MyStates.Ready;
}
});
}
private void ButtonAdd_Click(object sender, RoutedEventArgs e)
{
TestItems.Add(new TestItem());
}
}
}
AnimationTestControl.xaml:
<UserControl x:Class="AnimationTest.AnimationTestControl"
x:Name="self"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:AnimationTest"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid DataContext="{Binding ElementName=self}">
<StackPanel Orientation="Vertical">
<ItemsControl ItemsSource="{Binding TestItems}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Border Width="10" Height="10"
CornerRadius="5"
Background="Red"
BorderBrush="White"
BorderThickness="1">
<Border.Style>
<Style TargetType="Border">
<Setter Property="Opacity" Value="0.0"/>
<Style.Triggers>
<DataTrigger Binding="{Binding MyState}" Value="{x:Static local:MyStates.Initializing}">
<DataTrigger.EnterActions>
<StopStoryboard BeginStoryboardName="Animate"/>
<BeginStoryboard x:Name="Animate" HandoffBehavior="SnapshotAndReplace">
<Storyboard Duration="0:0:0.4">
<DoubleAnimation AutoReverse="True" Storyboard.TargetProperty="Opacity" To="1.0" Duration="0:0:0.2" FillBehavior="Stop" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
</Border>
<TextBlock Text="{Binding MyState}"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<StackPanel Orientation="Horizontal">
<Button Content="Start" Click="ButtonStart_Click" />
<Button Content="Add" Click="ButtonAdd_Click" />
</StackPanel>
</StackPanel>
</Grid>
</UserControl>
【问题讨论】:
-
这令人沮丧:为什么这篇文章被否决了?它有代码,一个明确的问题,显示了研究工作......我怎样才能防止这种情况再次发生?如果它被标记为可能重复 - 好吧,也许,但是否决票?
-
我知道你的感受。但请记住,说你酷的人更多。
-
什么时候不起作用,即如何重现您的问题?
-
很可能是因为您没有取消已启动的动画并且它继续保持该属性的值。要准确理解您的问题,请显示足以运行和识别问题的最少可重现代码。