You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

VSTO Outlook:如何获知Popup最终选定的放置位置?

WPF Popup 自定义位置后获取最终选定位置的方法

WPF没有直接提供原生事件或属性来告知CustomPopupPlacementCallback返回的候选位置中最终被选中的那一个,但可以通过两种可行方案实现需求:

方案一:在回调内模拟WPF的位置选择逻辑并记录

CustomPopupPlacementCallback的参数包含位置相关信息,你可以模拟WPF基于屏幕边界自动选位的逻辑,同时记录下最终会被选中的候选位置。

示例代码:

private CustomPopupPlacement[] CustomPopupPlacementHandler(Size popupSize, Size targetSize, Point offset)
{
    // 预定义候选位置列表
    var candidatePlacements = new[]
    {
        new CustomPopupPlacement(new Point(0, targetSize.Height), PopupPrimaryAxis.Horizontal),
        new CustomPopupPlacement(new Point(targetSize.Width, 0), PopupPrimaryAxis.Vertical),
        new CustomPopupPlacement(new Point(0, -popupSize.Height), PopupPrimaryAxis.Horizontal),
        new CustomPopupPlacement(new Point(-popupSize.Width, 0), PopupPrimaryAxis.Vertical)
    };

    // 模拟WPF的选位逻辑,确定最终选中项
    var selected = DetermineSelectedPlacement(candidatePlacements, popupSize, targetSize, offset);
    // 这里可将selected存入变量、触发自定义事件或进行其他处理
    Debug.WriteLine($"最终选中位置:{selected.Point}");

    return candidatePlacements;
}

private CustomPopupPlacement DetermineSelectedPlacement(CustomPopupPlacement[] placements, Size popupSize, Size targetSize, Point offset)
{
    // 简化版WPF选位逻辑,若需完全匹配原生逻辑可参考WPF源码实现
    var workingArea = System.Windows.Forms.Screen.FromHandle(new WindowInteropHelper(Application.Current.MainWindow).Handle).WorkingArea;
    
    foreach (var placement in placements)
    {
        var popupRect = new Rect(
            offset.X + placement.Point.X,
            offset.Y + placement.Point.Y,
            popupSize.Width,
            popupSize.Height);

        // 检查弹窗是否完全处于工作区内
        if (workingArea.Contains(popupRect))
        {
            return placement;
        }
    }
    // 若所有候选都超出边界,返回第一个候选
    return placements[0];
}

方案二:监听Popup的Opened事件,匹配最终位置与候选列表

Popup打开后,通过ActualWidth/ActualHeight和HorizontalOffset/VerticalOffset可获取其最终屏幕位置,再与预定义的候选位置列表进行匹配,找到对应的项。

示例代码:

private void Popup_Opened(object sender, EventArgs e)
{
    var popup = sender as Popup;
    if (popup == null || popup.PlacementTarget == null) return;

    // 获取Popup和目标控件的屏幕坐标
    var popupScreenPos = popup.PointToScreen(new Point(0, 0));
    var targetScreenPos = popup.PlacementTarget.PointToScreen(new Point(0, 0));
    var targetRenderSize = popup.PlacementTarget.RenderSize;

    // 需与回调内定义的候选列表保持一致
    var candidatePlacements = new[]
    {
        new CustomPopupPlacement(new Point(0, targetRenderSize.Height), PopupPrimaryAxis.Horizontal),
        new CustomPopupPlacement(new Point(targetRenderSize.Width, 0), PopupPrimaryAxis.Vertical),
        new CustomPopupPlacement(new Point(0, -popup.ActualHeight), PopupPrimaryAxis.Horizontal),
        new CustomPopupPlacement(new Point(-popup.ActualWidth, 0), PopupPrimaryAxis.Vertical)
    };

    // 匹配最终位置对应的候选项
    foreach (var placement in candidatePlacements)
    {
        var expectedPos = new Point(
            targetScreenPos.X + placement.Point.X,
            targetScreenPos.Y + placement.Point.Y);

        // 考虑渲染精度误差,使用近似匹配
        if (Math.Abs(popupScreenPos.X - expectedPos.X) < 1 && Math.Abs(popupScreenPos.Y - expectedPos.Y) < 1)
        {
            Debug.WriteLine($"最终选中位置:{placement.Point}");
            break;
        }
    }
}

注意事项

  • 方案一的模拟逻辑与WPF原生选位逻辑可能存在细微差异,若需完全一致,可参考WPF官方源码中Popup的位置选择实现。
  • 方案二中,若Opened触发时Popup布局未完成,可通过Dispatcher.BeginInvoke延迟执行匹配逻辑,避免ActualWidth/ActualHeight值不准确。

内容的提问来源于stack exchange,提问作者Willy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 14:40:48