'How to prevent a popup Form from exceeding screen area?
I am building a two column custom context menu layout using Forms.
I named the class of the form of the custom context menu as ContextMenu
.
I created a flag function to check whether it exceeds the device screen dimension upon invocation.
Single cases like exceeding only the length or only the height of device screen works.
However, when it comes to exceeding both the length and height of device screen, it somehow didn't work. I've tried to print the values in the console for checking purposes(example). The values printed are correct, but for some reason, it didn't enter the switch
case for it.
Did I missed something anywhere in the codes?
Below is the flag function. case 3
doesn't work(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;
}
Below is the event handler, which calls the above function:
//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!";
}
}
}
Solution 1:[1]
You may want to consider that the current mouse Pointer location is returned by MouseEventArgs.Location
. You just need to convert this value to Screen coordinates using the [Control].PointToScreen() method.
Then compare the initial Location - plus the Width
and Height
of your Popup - with the value returned by Screen.FromControl([Control]).WorkingArea and verify whether the Popup is contained within this bounds.
If it's not, then subtract the Popup's Width
and/or Height
from the WorkingArea
left and right bounds.
Something like this:
Note that these measures don't include the Form's invisible borders. The distance from the screen's sides will be 14~16 pixels. Adjust it if you prefer a tighter position
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;
}
Your app needs to be DpiAware to receive reliable measures and coordinates.
See the notes in Using SetWindowPos with multiple monitors
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 |