为什么WPF弹出PlacementRectangle会导致剪切?

本文关键字:WPF 弹出 PlacementRectangle 为什么 | 更新日期: 2023-09-27 17:49:45

在我正在开发的WPF应用程序中,我在地图上有图标,当单击时在弹出框中显示上下文菜单。因为我想在图标下方对齐上下文菜单,所以我使用PlacementRectangle与图标图像和Placement.Bottom的矩形。

这在大多数情况下工作良好,但少数图标的上下文菜单超过屏幕高度的一半(在1024x768屏幕上),当接近屏幕边缘时,弹出框在与边缘对齐后被剪切。

我已经阅读了几次关于这个的文档,但没有看到我所看到的行为的解释。具体来说,我看到的行为是:

当图标接近屏幕中央,但太低而无法容纳菜单时,弹出框会按预期翻转到图标的顶部(Target Origin &弹出对齐点更改)。当结果翻转使弹出窗口的顶部超过屏幕的上边缘时,弹出窗口的顶部将按照预期与屏幕重新对齐。然而,弹出窗口的底部边缘不会随着弹出窗口的其余部分向下移动,从而导致弹出窗口的底部内容被剪切(虽然有足够的扩展空间)。这似乎违背了它首先被移到屏幕边缘的原因("出于安全考虑"…)。

从文档中,我希望所有这些行为都是相同的,除了最后的剪辑,如果我使用Placement.MousePlacement.MousePoint,整个弹出式正确移动。

我在msdn文档或谷歌搜索中找不到这种行为的解释,这是一个错误还是预期的行为?是否有一个解决方案或技巧使用PlacementRectangle和防止由此产生的剪切?

我创建了一个简单的WPF示例应用程序来演示这个问题。它只是一个带有画布的窗口,其中包含一个名为"Rec"的矩形。该代码有一个事件处理程序:
    private void Window_MouseDown( object sender, MouseButtonEventArgs e )
    {
        // get mouse coordinates at click
        var c = Mouse.GetPosition(this);
        var cs = PointToScreen(c);
        // an image to visualize popup clipping
        var img = new Image() { Source = new BitmapImage( new Uri( "pack://application:,,,/test.png" ) ) };
        img.Width = 150;
        img.Height = 500;
        // visually show the placement rectangle
        Canvas.SetLeft( Rec, c.X );
        Canvas.SetTop( Rec, c.Y );
        var p = new Popup()
        {
            Placement = PlacementMode.Bottom,
            PlacementRectangle = new Rect( cs.X, cs.Y, 40, 60 ),
            //Placement = PlacementMode.Mouse,
            StaysOpen = false,
            Child = img,
        };
        p.IsOpen = true;
    }

注意:我尝试使用PlacementTarget并得到相同的剪切行为。

为什么WPF弹出PlacementRectangle会导致剪切?

由于没有人回答这个问题,我还没有找到任何关于这个问题的更多信息,我猜这是一个错误。我已经找到了一个解决方案,我想我会发布,以防其他人遇到它:

使用带有放置回调的自定义放置可以让您模仿弹出框的预期行为,并且不执行任何剪切:

    var p = new Popup()
    {
        PlacementRectangle = new Rect( cs.X, cs.Y, 40, 60 ),
        Placement = PlacementMode.Custom,
        CustomPopupPlacementCallback = new CustomPopupPlacementCallback( placePopup ),
        StaysOpen = false,
        Child = img,
    }; 

和我使用的位置回调:

private CustomPopupPlacement[] placePopup( Size popupSize, Size targetSize, Point offset )
{
    Debug.WriteLine( "{0}; {1}; {2}", popupSize.ToString(), targetSize.ToString(), offset.ToString() );
    return new[] { 
        new CustomPopupPlacement( new Point( -50, targetSize.Height ), PopupPrimaryAxis.Vertical ),
        new CustomPopupPlacement( new Point( -50, -popupSize.Height ), PopupPrimaryAxis.Vertical ),
    };
}