设置边距时,动画偶尔闪烁(在动画之后)

问题描述 投票:1回答:1

我正在创建一个由ToggleButtonContextMenu组成的下拉按钮。单击按钮后,菜单打开,并使用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);
}

这很好,并使菜单显示为“滑出按钮”:

Dropdown animation as intended but without dropshadow

旁注:

  • 我使用自定义动画,因为默认的PopupAnimation.Slide不能满足我的时间要求
  • 我知道我也可以为ContextMenu.VerticalOffsetProperty设置动画,但这会使菜单在动画过程中与按钮重叠。另一方面,对RenderTransform进行动画处理似乎在执行我想要的操作。

现在我在菜单中添加了一个阴影,它要求ContextMenu有一个边距(否则该阴影将被剪切)。这种方式破坏了菜单的对齐方式,因为它现在在动画过程中与按钮重叠(我想首先避免它):

Menu with dropshadow overlapping the button

因此,我想到了在动画过程中从菜单中删除上边距,然后像下面这样还原它的想法:

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

这在大多数情况下都是按预期方式工作的-动画过程中弹出窗口完美对齐,动画完成后,顶部阴影在顶部可见。但是,在恢复边距时,弹出窗口有时会非常短暂地移出位置:

Menu with shadow, moving out of place sporadically

[我知道更改MarginContextMenu属性将导致布局系统重新评估弹出窗口的位置。但是我不知道为什么有时在更新展示位置之前会有这个中间框架。另外,我不太了解为什么实际上增加了顶部边距时弹出窗口会向上跳。

更新:具有讽刺意味的是,在速度较慢的计算机上,观察到该行为的频率明显降低...

关于这个问题,我有什么可以做的吗?

一如既往,非常感谢!

wpf animation popup dropdown
1个回答
0
投票

好的,我想我想出了一种解决方法。

我在整个过程中都保持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);
}
© www.soinside.com 2019 - 2024. All rights reserved.