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

时间:2011-12-16 20:24:18

标签: c# wpf

我创建了一个名为“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事件,其中I:

  1. 检查淡入淡出是否已经发生,如果
  2. 则排队项目
  3. 开始淡出
  4. 看起来像这样:

    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();
    }
    

    由于一些奇怪的原因,从故事板内部完成事件调用ProcessQueue似乎打破了故事板。但只有在执行了正确的淡化序列时才会执行。

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

    在我正在使用ControlFader的窗口中,为了触发淡入淡出,我将绑定到ControlFader的SelectedIndex。从索引0到1的淡入之后,绑定停止更新,然后再返回到0。

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

    我很难过。有什么建议吗?

1 个答案:

答案 0 :(得分:1)

我的ProcessQueue方法的最后一行是问题:

SelectedIndex = itemIndex;

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

我通过使用所选项目来修复它:

SelectedItem = nextItem;

由于我从未在SelectedItem上设置任何绑定表达式,因此永远不会遇到错误。