首页 > 解决方案 > 停止 wpf 动画失败并出现 InvalidCastException

问题描述

我有一些显示 int 值的骰子,我正在尝试为它们设置动画,同时等待显示新的随机整数。我有一个可观察的 DieViewModel 集合和一个数据模板来可视化它们。当我开始掷骰子(以编程方式)时,我将 DieViewModels 上的“IsRolling”属性设置为 true,这会触发动画。动画快速切换骰子值。到这里为止它工作得很好。

当我获得骰子的新值时,我清除集合并添加新的 DieViewModels。然后我触发 PropertyChangedEvent 来告诉视图发生了一些变化。在这一点上,我得到了一个 InvalidCastException。没有动画它工作得很好。如果我为除(绑定)值之外的任何其他属性设置动画,它就可以正常工作。

我找到了解决方法,但我不明白这个问题。如果我不清除 oberservable 集合,保留旧的 DieViewModels 并将所有属性设置为新的 DieViewModels,它可以工作。但是从另一个列表更新列表元素的代码非常难看。而且我仍然需要 Dice.AddRange 部分来初始化列表,但必须确保它只运行一次。...除了新的 diceList 包含的元素比旧的多...我不喜欢这个解决方案

数据模板:

 <DataTemplate x:Key="DieTemplate" DataType="gameElements:DieViewModel">
     <Button>
         <Label x:Name="NumberLabel" 
                Content="{Binding Value}"/>
     </Button>
     <DataTemplate.Resources>
         <Storyboard x:Key="DieRollingAnimation">
             <Int32Animation Storyboard.TargetName="NumberLabel"
                             Storyboard.TargetProperty="Content"
                             From="1" To="6" Duration="0:0:0.6" 
                             RepeatBehavior="Forever"/>
         </Storyboard>
     </DataTemplate.Resources>
     <DataTemplate.Triggers>
         <DataTrigger Binding="{Binding IsRolling}" Value="True">
             <DataTrigger.EnterActions>
                 <BeginStoryboard
                     x:Name="beginAnimation"
                     Storyboard="{StaticResource DieRollingAnimation}" />
             </DataTrigger.EnterActions>
             <DataTrigger.ExitActions>
                 <StopStoryboard
                     BeginStoryboardName="beginAnimation" />
             </DataTrigger.ExitActions>
         </DataTrigger>
     </DataTemplate.Triggers>
 </DataTemplate>

调用更新骰子集合的方法:

private void UpdateDice(IEnumerable<DieViewModel> newDice)
{
    Dice.Clear();
    Dice.AddRange(newDice);
    RaisePropertyChanged(nameof(Dice));
}

我试图在重新初始化列表之前停止动画:

private void UpdateDice(IEnumerable<DieViewModel> newDice)
{
    for (int i = 0; i < Dice.Count; i++)
    {
        Dice[i].IsRolling = false;
    }

    Dice.Clear();
    Dice.AddRange(newDice);
    RaisePropertyChanged(nameof(Dice));
}

动画停止,但是当我提出 PropertyChanged 时问题仍然存在。

我已经好几年没有写过 for 循环了,我认为这不是 2020 年的 c# 风格。因此,如果我的问题没有其他解决方案,至少有人可以告诉我是否有办法使用 LINQ 实现这一目标?

这应该至少是一个 LINQ 工作:

 if (Dice.Count > 0 && Dice.Count == newDice.Count)
 {
     for (int i = 0; i < Dice.Count; i++)
     {
         Dice[i].IsRolling = false;
         Dice[i].Value = newDice[i].Value;
         // update some more properties
     }                
 }

标签: wpfanimation

解决方案


Storyboard在清除集合之前,您必须通过设置IsRolling为 来停止falseStoryboard仍然绑定到项目的属性IsRolling

for 循环的 LINQ 变体(需要参考System.ValueTuple):

Dice.Take(Dice.Count)
  .Zip(newDice.Take(Dice.Count), (oldDice, newDice) => (oldDice, newDice)  
  .ToList()
  .ForEach(dicePair => 
    {
      dicePair.oldDice.IsRolling = false;
      dicePair.oldDice.Value = dicePair.newDice.Value;
    });

for 循环仍然有效"2020 c# style"。在大多数情况下,它的性能要好得多。


推荐阅读