设置边距时弹出窗口偶尔闪烁(动画后)

Popup sporadically flickers when setting margin (after animation)

我正在创建一个由 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);
}

这工作正常并使菜单显示 "slide out of the button":

旁注:

现在我在菜单中添加了阴影,这需要 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);
}

这在大多数情况下都按预期工作 - 弹出窗口在动画期间完美对齐,并且在动画结束后阴影在顶部可见。但是,在恢复边距时,弹出窗口有时会非常短暂地移动到位:

我知道更改 ContextMenuMargin 属性 将导致布局系统重新评估弹出窗口的位置。但是不知道为什么有时候placement更新之前会有这个中间帧。另外,我不太明白为什么在上边距实际增加时弹出窗口会向上跳。

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

关于这个问题我能做些什么吗?

一如既往,非常感谢您!

好的,我想我找到了解决方法。

我将在整个过程中保持 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);
}