【发布时间】:2012-01-22 07:21:08
【问题描述】:
我创建了一个自定义控件,我称之为“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 事件,我:
- 检查淡入淡出是否已经发生,如果是则将项目排队
- 开始淡入淡出
看起来像这样:
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 不同步(它们落后),因为队列没有被处理。
我被难住了。有什么建议吗?
【问题讨论】:
-
ListBoxItem有Selected和Unselected视觉状态,为什么不使用它们呢? -
@Xin 有趣的建议,但是如果其中一个状态动画正在发生,我将如何阻止进一步的选择(将下一个项目添加到队列中)?
-
在状态动画期间禁用选择怎么样?
-
@Xin 在我的控件的一次使用中,选定的索引被绑定到一个指示下载图像状态的枚举。当处于状态 1(正在下载)时,ControlFader 显示一个进度条,当处于状态 2(已下载)时,ControlFader 应该从进度条淡出到实际图像。还有其他状态和触发它们的其他原因(用户交互,例如在下载过程中点击刷新)。由于状态可以经常且迅速地改变,我不能简单地忽略或阻止所选索引被更新。这就是我引入队列的原因。
-
我不明白为什么状态动画必须在项目选择之间“完成”。为什么不能发生这种情况:用户点击Item1(Item1开始淡入);用户快速点击Item2(Item1没有完成fadeIn只是开始fadeOut,Item2开始fadeIn);用户快速点击Item1(Item2开始淡出,Item1开始淡入)