通过队列动画和重新动画控件

本文关键字:动画 控件 新动画 队列 | 更新日期: 2023-09-27 18:18:18

我创建了一个自定义控件ControlFader。它继承自Selector原语。添加到控件中的项作为重叠控件放置到单个网格中。我们的目标是有一个动画,其中选择的项目是淡入的,而以前选择的项目是淡入的。

首先,我提供了一个自定义项目容器(类型为ControlFaderItem),其不透明度设置为零。下面的代码用于创建故事板(FadeTime在别处定义):

private Storyboard CreateStoryboard(ControlFaderItem sourceItem, ControlFaderItem targetItem)
{
    var fadeControlsStoryboard = new Storyboard();
    if (sourceItem != null)
    {
        var sourceAnimation = new DoubleAnimation(1, 0, FadeTime);
        sourceAnimation.SetValue(Storyboard.TargetProperty, sourceItem);
        sourceAnimation.SetValue(Storyboard.TargetPropertyProperty, new PropertyPath(UIElement.OpacityProperty));
        fadeControlsStoryboard.Children.Add(sourceAnimation);
    }
    if (targetItem != null)
    {
        var targetAnimation = new DoubleAnimation(0, 1, FadeTime);
        targetAnimation.SetValue(Storyboard.TargetProperty, targetItem);
        targetAnimation.SetValue(Storyboard.TargetPropertyProperty, new PropertyPath(UIElement.OpacityProperty));
        fadeControlsStoryboard.Children.Add(targetAnimation);
    }
    return fadeControlsStoryboard;
}

我添加了一个队列来指示下一个要褪色的项目。使用以下方法从队列中删除项:

private void ProcessQueue()
{
    if (isFadeAnimating)
        return;
    // can't process if there are no queued items
    if (FadeQueue.Count == 0)
        return;
    // get the next item to fade to
    var nextItem = FadeQueue.Dequeue();
    // locate the index of the item
    var itemIndex = Items.IndexOf(nextItem);
    if (itemIndex != -1)
        SelectedIndex = itemIndex;
}

我重写OnSelectionChanged事件,其中我:

  1. 检查淡出是否已经发生,如果是
  2. ,则队列项。
  3. 开始淡出

它看起来像这样:

protected override void OnSelectionChanged(SelectionChangedEventArgs e)
{
    // code to simulate an OnSelectionChanging is here (removed for brevity)
    // it's used to queue a new selection if isFadeAnimating is true, and reverse the selection
    // code to obtain source and target container item also removed for brevity
    if (isFadeAnimating)
        return;
    isFadeAnimating = true;
    var storyboard = CreateStoryboard(sourceItem, targetItem);
    storyboard.Completed += (s, arg) =>
    {
        if (sourceItem != null)
            sourceItem.Opacity = 0d;
        isFadeAnimating = false;
        base.OnSelectionChanged(e);
        ProcessQueue();
    };
    storyboard.Begin();
}

由于一些奇怪的原因,从storyboard完成事件内部调用ProcessQueue似乎破坏了storyboard。但前提是执行了正确的淡出顺序。

如果我从索引0淡出到1,然后足够快地回到0,淡出停止工作。它必须足够快,以便在第一次淡出发生时尝试退隐回0(因此将其放在队列中)。

在我使用ControlFader的窗口中,为了触发淡出,我绑定到ControlFader的selecteindex。当索引从0变为1,然后再变为0时,绑定停止更新。

当我移除对ProcessQueue的调用时,动画和绑定永远不会中断,但是我的淡出并不同步(它们落后),因为队列没有被处理,应该是当前的selecteindex。

我难住了。有什么建议吗?

通过队列动画和重新动画控件

ProcessQueue方法中的最后一行是问题所在:

SelectedIndex = itemIndex;

因为我在SelectedIndex上设置了绑定,通过在控件代码中分配它,我的绑定被删除了。由于这是在ProcessQueue方法中,所以只有当有队列项时才会出现问题。

我用selected item代替

SelectedItem = nextItem;

由于我从未在SelectedItem上设置任何绑定表达式,因此该错误从未被击中。