如何防止弹出窗体超出屏幕区域?

How to prevent a popup Form from exceeding screen area?

我正在使用 Forms 构建一个两列的自定义上下文菜单布局。
我将自定义上下文菜单的形式 class 命名为 ContextMenu.

我创建了一个标志函数来检查它是否在调用时超出设备屏幕尺寸。
仅超出设备屏幕的长度或高度等个别情况有效。

但是,当涉及到超过设备屏幕的长度和高度时,它不知何故不起作用。我试图在控制台中打印值以进行检查(example)。打印的值是正确的,但由于某种原因,它没有进入 switch 案例。

我是否遗漏了代码中的任何地方?

下面是标志函数。 case 3 无效(example)。

//exceed screen flag function
private Point processContextMenuFormLocation(ContextMenu theContextMenu, int screenExceedFlag, Point mouseCoor)
{
    //local form coordinate
    int formXCoor;
    int formYCoor;

    switch (screenExceedFlag)
    {
        case 1: //if exceed right boundary
            formXCoor = (mouseCoor.X) - (contextMenuObj.Width); //move context menu to the left
            formYCoor = mouseCoor.Y; //no need changes
            //after exceedFlag, set where context menu position is
            theContextMenu.Location = new Point(formXCoor, formYCoor);
            break;
        case 2: //if exceed bottom boundary
            formXCoor = (mouseCoor.X); //no need changes
            formYCoor = (mouseCoor.Y) - (contextMenuObj.Height); //move context menu to the top
            theContextMenu.Location = new Point(formXCoor, formYCoor);
            break;
        case 3: //if exceed right & bottom boundary
            formXCoor = (mouseCoor.X) - (contextMenuObj.Width); //move context menu to the left
            formYCoor = (mouseCoor.Y) - (contextMenuObj.Height); //move context menu to the top
            theContextMenu.Location = new Point(formXCoor, formYCoor);
            break;
        case -1: //if exceeded nothing
            theContextMenu.Location = new Point(mouseCoor.X, mouseCoor.Y);
            break;
    }
    //return the new location of context menu
    return theContextMenu.Location;
}

下面是调用上面函数的事件处理器:

//mouse up event handler
private void richTextBox1_MouseUp(object sender, MouseEventArgs e)
{
    //set default CM closed so that won't open concurrently
    richContextStrip.Visible = false;

    if (e.Button == MouseButtons.Right)
    {
        IsKeyUp((int)MouseButtons.Right); //set rmbIsUp = true 

        //here because the rmb event handler is above this
        bool canDisplay = contextMenuDisplayFlag(ctrlIsDown, rmbIsUp); //get ctrl and rmb flag status 

        if (canDisplay)
        {
            //to process whether custom context menu was opened beyond screen area or not
            exceedScreenFlag = isBeyondScreen(Cursor.Position.X, Cursor.Position.Y, contextMenuObj.Width, contextMenuObj.Height);

            //obtain mouse coordinates
            Point theMouseCoor = new Point(Cursor.Position.X, Cursor.Position.Y);

            //exceed screen flag function
            Point formLocation = processContextMenuFormLocation(contextMenuObj, exceedScreenFlag, theMouseCoor);

            //center the cursor at context menu
            Point cursorLocation = processContextMenuCursorLocation(contextMenuObj, formLocation);

            displayCustomContextMenu(contextMenuObj, formLocation, cursorLocation);
            toolStripStatusLabel1.Text = "Custom context menu opened!";
        }
        else //if ctrl key is not pressed
        {
            richContextStrip.Visible = true;
            toolStripStatusLabel1.Text = "Default context menu opened!";
        }
    }
}

您可能要考虑由MouseEventArgs.Location 返回当前鼠标指针位置。您只需要使用 [Control].PointToScreen() 方法将此值转换为屏幕坐标。

然后将初始位置 - 加上 Popup 的 WidthHeight - 与 Screen.FromControl([Control]).WorkingArea 返回的值进行比较,并验证 Popup 是否包含在此范围内。
如果不是,则从 WorkingArea 左右边​​界中减去 Popup 的 Width and/or Height

像这样:
请注意,这些措施不包括表单的不可见边框。与屏幕两侧的距离为 14~16 像素。如果您喜欢更紧的位置

,请调整它
private void someControl_MouseUp(object sender, MouseEventArgs e)
{
    var f = new Form() { Width = 400, Height = 400, StartPosition = FormStartPosition.Manual };
    f.Location = SetPopupLocation(Screen.FromControl(this), f, (sender as Control).PointToScreen(e.Location));
    f.Show();
}

private Point SetPopupLocation(Screen screen, Form form, Point initPosition)
{
    var p = new Point();
    var wrkArea = screen.WorkingArea;
    p.X = wrkArea.Right - (initPosition.X + form.Width);
    p.Y = wrkArea.Bottom - (initPosition.Y + form.Height);
    p.X = p.X < 0 ? wrkArea.Right - form.Width : initPosition.X;
    p.Y = p.Y < 0 ? wrkArea.Bottom - form.Height : initPosition.Y;
    return p;
}

您的应用需要 DpiAware 才能接收可靠的测量值和坐标。
请参阅

中的注释