我正在创建一个由ToggleButton
和ContextMenu
组成的下拉按钮。单击按钮后,菜单打开,并使用CustomPopupPlacementCallback
与按钮对齐。
我在菜单上将自定义动画应用于菜单,如下所示:
private void ContextMenu_Opened(object sender, RoutedEventArgs e)
{
ContextMenu contextMenu = (ContextMenu)sender;
NameScope.SetNameScope(this, new NameScope());
TranslateTransform translation = new TranslateTransform();
RegisterName("TranslateTransform", translation);
contextMenu.RenderTransform = translation;
DoubleAnimation translationAnimation = new DoubleAnimation()
{
From = -20,
To = 0,
Duration = new Duration(new TimeSpan(0, 0, 0, 0, 200)),
EasingFunction = new PowerEase() { EasingMode = EasingMode.EaseOut, Power = 3 }
};
Storyboard.SetTargetProperty(translationAnimation, new PropertyPath(TranslateTransform.YProperty));
Storyboard.SetTargetName(translationAnimation, "TranslateTransform");
Storyboard storyboard = new Storyboard();
storyboard.Children.Add(translationAnimation);
storyboard.Begin(this);
}
这很好,并使菜单显示为“滑出按钮”:
旁注:
PopupAnimation.Slide
不能满足我的时间要求ContextMenu.VerticalOffsetProperty
设置动画,但这会使菜单在动画过程中与按钮重叠。另一方面,对RenderTransform
进行动画处理似乎在执行我想要的操作。现在我在菜单中添加了一个阴影,它要求ContextMenu
有一个边距(否则该阴影将被剪切)。这种方式破坏了菜单的对齐方式,因为它现在在动画过程中与按钮重叠(我想首先避免它):
因此,我想到了在动画过程中从菜单中删除上边距,然后像下面这样还原它的想法:
private void ContextMenu_Opened(object sender, RoutedEventArgs e)
{
Thickness originalMargin = contextMenu.Margin;
contextMenu.Margin = new Thickness(
contextMenu.Margin.Left, 0, contextMenu.Margin.Right, contextMenu.Margin.Bottom);
/* Prepare animation as shown before... */
storyboard.Completed += new EventHandler((animation, eventArgs) =>
{
contextMenu.Margin = originalMargin;
});
storyboard.Begin(this);
}
这在大多数情况下都是按预期方式工作的-动画过程中弹出窗口完美对齐,动画完成后,顶部阴影在顶部可见。但是,在恢复边距时,弹出窗口有时会非常短暂地移出位置:
[我知道更改Margin
的ContextMenu
属性将导致布局系统重新评估弹出窗口的位置。但是我不知道为什么有时在更新展示位置之前会有这个中间框架。另外,我不太了解为什么实际上增加了顶部边距时弹出窗口会向上跳。
更新:具有讽刺意味的是,在速度较慢的计算机上,观察到该行为的频率明显降低...
关于这个问题,我有什么可以做的吗?
一如既往,非常感谢!
好的,我想我想出了一种解决方法。
我在整个过程中都保持Margin
属性不变。这避免了原来的问题,但是,如前所示,使菜单与按钮重叠(请参阅我的原始问题中的第二个动画)。
现在,我必须补偿不必要的偏移,但是我不能使用VerticalOffset
属性(因为在动画结束后将其删除会再次导致零星的闪烁)。取而代之的是,我使用剪切几何体并为其设置动画,以便它始终剪切菜单中与按钮重叠的部分:
private void ContextMenu_Opened(object sender, RoutedEventArgs e)
{
/* Prepare translation animation as shown before... */
RectangleGeometry clipGeometry = new RectangleGeometry(new Rect(
new Point(-contextMenu.Margin.Left, 0),
new Size(contextMenu.ActualWidth + contextMenu.Margin.Left + contextMenu.Margin.Right,
contextMenu.ActualHeight + contextMenu.Margin.Bottom)));
contextMenu.RegisterName("RectangleGeometry", clipGeometry);
contextMenu.Clip = clipGeometry;
RectAnimation clippingAnimation = new RectAnimation()
{
Duration = new Duration(new TimeSpan(0, 0, 0, 0, 200)),
EasingFunction = new PowerEase() { EasingMode = EasingMode.EaseOut, Power = 3 },
From = new Rect(
new Point(-contextMenu.Margin.Left, 20),
new Size(contextMenu.ActualWidth + contextMenu.Margin.Left + contextMenu.Margin.Right,
contextMenu.ActualHeight - 20 + contextMenu.Margin.Bottom))
};
SetTargetProperty(clippingAnimation, new PropertyPath(RectangleGeometry.RectProperty));
SetTargetName(clippingAnimation, "RectangleGeometry");
storyboard.Children.Add(clippingAnimation);
storyboard.Completed += new EventHandler((animation, eventArgs) =>
{
contextMenu.RenderTransform = null;
contextMenu.Clip = null;
});
storyboard.Begin(this);
}