Prevent new window from going offscreen - c#

If the MainWindow is too close to the edge of the screen, opening a New Window with relative positioning can go off screen.
I'd like to have it detect that it's off screen and reposition itself close to the edge, even overlapping the MainWindow. For Top, Bottom, Left and Right.
Example Project Source
https://www.dropbox.com/s/3r2guvssiakcz6f/WindowReposition.zip?dl=0
private Boolean IsWindowOpened = false;
// Info Button
//
private void buttonInfo_Click(object sender, RoutedEventArgs e)
{
MainWindow mainwindow = this;
// Start Info Window
InfoWindow info = new InfoWindow(mainwindow);
// Only Allow 1 Window Instance
if (IsWindowOpened) return;
info.ContentRendered += delegate { IsWindowOpened = true; };
info.Closed += delegate { IsWindowOpened = false; };
// Position Relative to MainWindow
info.Left = mainwindow.Left - 270;
info.Top = mainwindow.Top + 0;
// Open Info Window
info.Show();
}
Example of 1280x720 screen
MainWindow Center Screen
InfoWindow -270px Left, 0px Top
Off Screen
MainWindow Top Left of Screen
InfoWindow -270px Left, 0px Top
Reposition In Screen
MainWindow Top Left of Screen
InfoWindow -160px Left, 0px Top

To Place Dialog to Left
The quick-n-dirty way of doing this is to simply use Math.Max (i.e. the right-most value) to use the offset or 0, whichever is larger. Using System.Windows.Forms.Screen allows us to accommodate for multiple monitors.
private void btnInfoToLeft_Click(object sender, RoutedEventArgs e)
{
// Figure out which screen we're on
var allScreens = Screen.AllScreens.ToList();
var thisScreen = allScreens.SingleOrDefault(s => this.Left >= s.WorkingArea.Left && this.Left < s.WorkingArea.Right);
// Place dialog to left of window, but not past screen border
InfoWindow info= new InfoWindow();
info.Left = Math.Max(this.Left - info.Width - 10, thisScreen.WorkingArea.Left);
info.Top = Math.Max(this.Top - info.Height - 10, thisScreen.WorkingArea.Top);
info.Show();
}
Note that we use the Width of the dialog - it's ActualWidth will be 0 before it is shown on the screen.
To Place Dialog to Right
Similarly, we need to figure out the right-most boundaries of the screen, and account for the width of the main window and dialog, and take the Math.Min value (i.e. the left-most value).
private void btnInfoToRight_Click(object sender, RoutedEventArgs e)
{
// Figure out which screen we're on
var allScreens = Screen.AllScreens.ToList();
var thisScreen = allScreens.SingleOrDefault(s => this.Left >= s.WorkingArea.Left && this.Left < s.WorkingArea.Right);
// Place dialog to right of window, but not past screen border
InfoWindow info = new InfoWindow();
info.Left = Math.Min(this.Left + this.ActualWidth + 10, thisScreen.WorkingArea.Right - info.Width);
info.Top = Math.Min(this.Top + this.ActualHeight + 10, thisScreen.WorkingArea.Bottom - info.Height);
info.Show();
}
This time, we still use the Width of the dialog, but the ActualWidth of the main window, which will be the width after it has been drawn (and possibly resized).
In these examples, I've also placed the dialog above/below the main window. You can set the top of the dialog to be equal to the top of the main window, or play around to get it to line up with the bottom, etc., using this example as a guide.

There are no shortcuts for this kind of problem. You'll have to figure out the dimensions of the screen you're using, then adjust the position of the info window manually.
Take a look at this StackOverflow post: How to get the size of the current screen in WPF?

Related

C# Screen Class member methods cannot open form on secondary display

There are some methods from the Screen class to retrieve the respective screen, which contains the most area of a control.
They are the:
Screen.FromRectangle(Rectangle rect)
Screen.FromControl(Control control)
Screen.FromHandle(IntPtr hwnd)
Screen.FromPoint(Point point)
The issue here is that I have a problem getting my second Form to be opened in the second monitor. It kept on displaying only on the primary screen. According to the answer from here, the suspect is that the DpiAware was not enabled. However, the problem was still not fixed even after I have enabled the DpiAware.
To help me to check what was going on, I've added code for debugging. Then, I called/ opened the popup menu on \\\\.\\DISPLAY2. Inside of the debug console, the chosen Screen for FromPoint() was written \\\\.\\DISPLAY2. However, the popup menu was still opened on \\\\.\\DISPLAY1. Can anyone help me with this issue, please?
Below are the codes example:
Under main form class:
//mouseup event handler
private void Form1_MouseUp(object sender, MouseEventArgs e)
{
//if RMB clicked
if(e.Button == MouseButtons.Right)
{
//----set PopupMenu form location----
popupMenuObj.Location = popupMenuObj.popupMenuLocation((sender as Control).PointToScreen(e.Location));
//----set PopupMenu form location----
//process the form location
popupMenuObj.Location = popupMenuObj.processFormLocation(popupMenuObj.Location);
//----set PopupMenu cursor location----
Cursor.Position = popupMenuObj.middleOfPopupMenu(popupMenuObj.Location);
//----set PopupMenu cursor location----
//----display the popup menu----
popupMenuObj.displayPopupMenu(1);
//----display the popup menu----
}
}
Under popup menu class:
//to set cursor to middle of popup menu
public Point middleOfPopupMenu(Point mousePoint)
{
int theCenterWidth = this.Width / 2;
int theCenterHeight = this.Height / 2;
//set the mouse pointer location at middle of PopupMenu
mousePoint.X += theCenterWidth;
mousePoint.Y += theCenterHeight;
return Cursor.Position = mousePoint;
}
//to set popupmenu location
public Point popupMenuLocation(Point thePoint)
{
return this.Location = thePoint;
}
//set popup menu display status
public bool displayPopupMenu(int displayFlag)
{
switch(displayFlag)
{
case 1: //if argument 1 -> display the PopupMenu
this.Visible = true;
return true;
case 0: //if argument 0 -> close the PopupMenu
this.Visible = false;
return false;
default: //by default, no need to display
return false;
}
}
//to process if popupmenu opens exceeding the screen working area
public Point processFormLocation(Point currentPoint)
{
Point newPoint = new Point();
int selectedScrWidth, selectedScrHeight; //screen dimension of the selected screen
Screen selectedScreen;
int totalXPos, totalYPos; //where the form is located before exceed screen is handled
//---get screen where point is at---
selectedScreen = Screen.FromPoint(currentPoint);
Console.WriteLine("\nSelected screen info: {0}\n", selectedScreen); //for debugging purposes
//---get screen where point is at---
//---get selected screen dimension---
selectedScrWidth = selectedScreen.WorkingArea.Width;
selectedScrHeight = selectedScreen.WorkingArea.Height;
//---get selected screen dimension---
totalXPos = currentPoint.X + this.Width; //mouseXCoor + popupMenu width
totalYPos = currentPoint.Y + this.Height; //mouseXCoor + popupMenu height
//if exceed selected screen width and height
if ((totalXPos >= selectedScrWidth) && (totalYPos >= selectedScrHeight))
{
newPoint.X = selectedScrWidth - this.Width;
newPoint.Y = selectedScrHeight - this.Height;
}
//if exceed selected screen width
else if ((totalXPos >= selectedScrWidth))
{
newPoint.X = selectedScrWidth - this.Width;
newPoint.Y = currentPoint.Y;
}
//if exceed selected screen height
else if ((totalYPos >= selectedScrHeight))
{
newPoint.X = newPoint.X;
newPoint.Y = selectedScrHeight - this.Height;
}
else //if x exceed screen, just set the location as is
{
return this.Location = Cursor.Position;
}
return this.Location = newPoint;
}
Update 1:
As per Jimi's request under the comment section, after creating a simple test project based on given answer here, here's what I got:
Please note the reason why the code in the question from the previous thread has no Width, Height, and StartPosition = FormStartPosition.Manual defined was because the popup menu is from a class on another .cs file where, those specific properties have already been declared in the PopupMenu.Designer.cs file. Therefore, I don't feel like redefining was necessary.
Update 2:
Now that it works, a newer problem arises is that, only when popup menu exceeds right boundary of secondary display, the popup menu was displayed on primary screen. However this didn't occur when I try to open the popup menu when exceeding the height of secondary screen. Example is shown below:
Above is the case when I try to open the popup menu when exceeding the width of secondary screen. The popup menu opens at the right boundary of primary screen.
It however worked just fine when I tried the case for exceeding the height of secondary display.

Maximizing borderless form with multiple monitors c#

I'm currently making a borderless form with a Doubleclick event to maximize form. But I realized that the form wouldn't maximize on the two other screens, only my main middle.
So my code is currently:
private void Form1_DoubleClick(object sender, EventArgs e)
{
if ((this.Height == Screen.PrimaryScreen.WorkingArea.Height) && (this.Width == Screen.PrimaryScreen.WorkingArea.Width))
{
this.Width = 534;
this.Height = 600;
CenterToScreen();
}
else
{
this.Height = Screen.PrimaryScreen.WorkingArea.Height;
this.Width = Screen.PrimaryScreen.WorkingArea.Width;
this.Location = Screen.PrimaryScreen.WorkingArea.Location;
}
}
It might look weird, but I use it to not cover the taskbar.
I need a code like this to dock it to the side, and use it to calculate where the form should be. Looking like this: half right screen dock
when I click one of those 9 buttons, it will dock the screen in different places of the screen. In corner, half of the screen or in the middle.
I tried using a code where the form would detect which screen it was on, and using that again to maximize the form on that screen, but I got a bunch of red lines, and it didn't work in the end.
I have 3 monitors.
Please help.
You hardcoded it to primary screen, which is the screen with the task bar. To allow other screens get the screen the form is currently on an adjust to that.
private void Form1_DoubleClick(object sender, EventArgs e)
{
if ((this.Height == Screen.FromControl(this).WorkingArea.Height) && (this.Width == Screen.FromControl(this).WorkingArea.Width))
{
this.Width = 534;
this.Height = 600;
CenterToScreen();
}
else
{
this.Height = Screen.FromControl(this).WorkingArea.Height;
this.Width = Screen.FromControl(this).WorkingArea.Width;
this.Location = Screen.FromControl(this).WorkingArea.Location;
}
}

Prevent Window Boundary from Over Screen Boundary when SizeChange

How to prevent Window Boundary from over Screen Boundary when Window SizeChange in C# WPF?
(that's Restrict Window Boundary in Screen )
Use the Window's OnSizeChanged Event,
and do like this:
//get Screen's Width, Height
private double screenHeight = SystemParameters.FullPrimaryScreenHeight;
private double screenWidth = SystemParameters.FullPrimaryScreenWidth;
private void MultiToolWindow_OnSizeChanged(object sender, SizeChangedEventArgs e)
{
//when window RightBoundary over screen
if (this.Left + this.Width > screenWidth)
this.Width = screenWidth-this.Left; //shrink the width
//when window DownBoundary over screen
if (this.Top + this.Height > screenHeight)
this.Height = screenHeight-this.Top; //shrink the height
}
Note that when using this, Window's SizeToContent Property should be in Manual,
if not,
you can change it like this:
public void SomeMethod(){
//set to manual, this will invoke OnSizeChangedEvent at the same time but the shrink code won't work
this.SizeToContent = SizeToContent.Manual;
//this will invoke OnSizeChangedEvent and because now is manual the shrink code works
this.SizeToContent = SizeToContent.Manual;
}
do twice to ensure when window's original SizeToContent State is WidthAndHeight can take effect too,
first time will set it to Manual and the shrink code won't take effect,
and second time cause the state is manual so the shrink code will take effect.

Dynamic Margin on Window Drag

Ok so complete rewrite of the question due to lack of replies. I want a window that is drag-able but as it's being dragged, alter the margin to extend as far as the old position of the window. I.e. Window moves right X, extend margin left X. Now I've hit a few snags such as the window having it's edges cut off for some reason. Here's my code, let me know if you can spot anything!
private void Window_LocationChanged(object sender, EventArgs e)
{
double TmpLeft = Math.Abs(this.Left - WinLeft);
double TmpTop = Math.Abs(this.Top - WinTop);
if (this.IsLoaded)
{//depending on whether the window is moved left, right
if (this.Left > WinLeft)
{//depending on whether the window is moved up, down
if (this.Top > WinTop)
bdr.Margin = new Thickness(TmpLeft, TmpTop, 0, 0);
else
bdr.Margin = new Thickness(TmpLeft, 0, 0, TmpTop);
}
else
{
if (this.Top > WinTop)
bdr.Margin = new Thickness(0, TmpTop, TmpLeft+ 40, 0);
else
bdr.Margin = new Thickness(0, 0, TmpLeft, TmpTop);
}
}
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
WinLeft = this.Left;
WinTop = this.Top;
bdr.Height = this.ActualHeight;//I set these because they are auto
bdr.Width = this.ActualWidth; //before the window opens
}
At the moment the whole window (set window background to yellow so I can see the margin) is moving, where as I want the top left corner of the margin to remain in place. I've also noticed that the area I click on to drag the window (a border) tends to move around as well so is no longer under my click as I move the window. Hope that's clear, comment any further questions.
OLD - ONLY READ TO UNDERSTAND WHAT I'M TRYING TO DO
So I'm trying to create an application that has a pop up window with a pointer/line coming from the child window to a particular place in the parent window. I achieved this like so;
<Border Name="brdr" Margin="40,0,0,0" >
//Content
</Border>
<Line
Name="Pointer"
X1="0"
X2="40"
Y1="55"
Y2="50"
StrokeThickness="2"
Stroke="Black"
></Line>
Notice the 40 left Margin on the border that makes the window larger than it appears so that the Polygon sticks out to the left and points to the parent window (If there's a better/cooler/more elegant way of doing this, I'm all ears).
So that worked fine but now I want the pointer to be dynamic. As in, if the child window gets dragged the pointer must scale relatively to the parent window's location. I.e. it must appear as if the two windows are attached via the line. Now my plan was to record the point the child window opens on (because it opens relative to the parent window, it's correct when it initialises) and then use the difference between this and the new location point (after dragging) to find the new points that the line should be going to. My code probably says it better than I ever could...
private void Window_LocationChanged(object sender, EventArgs e)
{
if (this.IsLoaded)
{
brdr.Margin = new Thickness(Math.Abs(this.Left - WinLeft) + 40, Math.Abs(this.Top - WinTop), 0, 0);
Pointer.X1 = Math.Abs(this.Left - WinLeft);
Pointer.Y1 = Math.Abs(this.Top - WinTop) + 55;
Pointer.X2 = Math.Abs(this.Left - WinLeft) + 40;
Pointer.Y2 = Math.Abs(this.Top - WinTop) + 50;
}
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
WinLeft = this.Left;
WinTop = this.Top;
}
As you can see I have to set the window margin so that it extends to the old position. Then I reset the Line coords to the new values. All these values are calculated, like I said, by comparing the opening window coords to the current coords.
My problem is, this isn't right. Would be very impressed to see someone able to figure this out.
i m surprised there's no code to switch from in-window coordinates to screen coordinates.
In a project of mine, i had to place a window right under a Control. I used this to get the screen coordinates of the point at the middle of the control:
Point point = MyControl.PointToScreen(new Point((MyControl.ActualWidth / 2)
, MyControl.ActualHeight));
PresentationSource source = PresentationSource.FromVisual(MyControl);
double dpiX = (96 * source.CompositionTarget.TransformToDevice.M11);
double dpiY = (96 * source.CompositionTarget.TransformToDevice.M22);
XInScreenCoordinates = ((point.X * 96 / dpiX));
YInScreenCoordinates = ((point.Y * 96 / dpiY));
If i didn't do this, there would be an error in placement, the more to the right the more error.
ok a small app i did :
there's MainWindow, the main window with very simple content, a line
and a Button :
<Grid>
<Line x:Name="MyLine" Stroke="Black" StrokeThickness="4" />
<Button Name="MyButton" Margin="248,101,0,0"
HorizontalAlignment="Left" VerticalAlignment="Top"
Content="Button" />
</Grid>
then i did another window, of small size (300*300) named TestWindow.
TestWindow has no content or code behind.
the code behind for MainWindow :
public partial class MainWindow : Window
{
public TestWindow MyTestWindow;
public MainWindow()
{
InitializeComponent();
MyTestWindow = new TestWindow();
MyTestWindow.Show();
MyTestWindow.LocationChanged += new EventHandler(WinLocationChanged);
MyTestWindow.SizeChanged += new SizeChangedEventHandler ( WinSizeChanged);
this.LocationChanged += new EventHandler(WinLocationChanged);
this.SizeChanged += new SizeChangedEventHandler(WinSizeChanged);
}
void WinSizeChanged(object sender, SizeChangedEventArgs e) {
UpdateLine(); }
void WinLocationChanged(object sender, EventArgs e) {
UpdateLine(); }
void UpdateLine()
{
// 1. get Window's center in in this window coordinates
double CX_sc =0.0, CY_sc = 0.0;
GetWindowCoordinatesInCurrentWindow(MyTestWindow, ref CX_sc, ref CY_sc);
// 2. Get Center of target Control coordinates in this window coordinates
Point CenterButtonPoint = MyButton.TransformToAncestor(this).Transform(new Point(MyButton.ActualWidth / 2.0, MyButton.ActualHeight / 2.0));
//3. Change line's coord.
MyLine.X1 = CX_sc;
MyLine.Y1 = CY_sc;
MyLine.X2 = CenterButtonPoint.X;
MyLine.Y2 = CenterButtonPoint.Y;
}
void GetWindowCoordinatesInCurrentWindow(Window ChildWindow, ref double X, ref double Y)
{
Point CenterOfChildWindow = ChildWindow.PointToScreen(new Point((ChildWindow.ActualWidth / 2)
, ChildWindow.ActualHeight/2));
Point UpperLeftOfCurrentWindow = this.PointToScreen(new Point(0, 0));
PresentationSource source = PresentationSource.FromVisual(this);
double dpiX = ( source.CompositionTarget.TransformToDevice.M11);
double dpiY = (source.CompositionTarget.TransformToDevice.M22);
X = (( CenterOfChildWindow.X -UpperLeftOfCurrentWindow.X ) /dpiX);
Y = ( (CenterOfChildWindow.Y-UpperLeftOfCurrentWindow.Y ) / dpiY );
}
}
What it does is that whenever one of the window is moved or resized, it will draw a
line into MainWindow that 'links' the Button to the middle of the TestWindow.
OK OK i know this is not what you want :=), since the line cannot go outside of MainWindow. But the idea is to have a transparent Popup which covers the whole screen in which you do the same thing.
Actually found a great way of doing this. What I did was I set the child window's WindowState="Maximized" then placed everything except the Line in a Canvas and by following http://denismorozov.blogspot.ie/2008/01/drag-controls-in-wpf-using.html (great article) I could make it so that I could move around my window. I had hard coded the line so that when the window opens it's in the correct position. Since one of the points of the Line shouldn't move, it was just a matter of updating the other point in the Canvas_MouseMove event like so;
Point p = border.TransformToAncestor(this).Transform(new Point(0, 0));
Line.X2 = p.X;
Line.Y2 = p.Y + 50;
Very simple way of getting your window to display relative to the parent window if you want a line joining the two. It gives the appearance of moving the window, but your actually moving a control around a maximised transparent window.

How to fake mouse cursor position in Windows Forms C#?

I have this Windows Forms application with a simple balloon tooltip. Depending on the application's window location on the desktop and the mouse cursor location, the balloon 'tip' (or balloon pointing arrow) may or may not be pointing to the location I want.
For instance, my app snaps to the desktop sides and when it's snapped to the right side, if the mouse cursor is below 100px of the right side, the balloon 'tip' will point to the wrong place. But if the mouse cursor is anywhere else, it will point to the right place.
In this situation I wanted to fake the mouse cursor position (without actually changing the mouse cursor position) to be somewhere else so the the problem wouldn't occur.
Is this possible? How can I achieve this?
private void noteTitleInput_KeyPress(object sender, KeyPressEventArgs e) {
if(e.KeyChar == Convert.ToChar(Keys.Return, CultureInfo.InvariantCulture) && noteTitleInput.Text.Length > 0) {
e.Handled = true;
noteInputButton_Click(null, null);
} else if(!Char.IsControl(e.KeyChar)) {
if(Array.IndexOf(Path.GetInvalidFileNameChars(), e.KeyChar) > -1) {
e.Handled = true;
System.Media.SystemSounds.Beep.Play();
noteTitleToolTip.Show("The following characters are not valid:\n\\ / : * ? < > |",
groupNoteInput, 25, -75, 2500);
return;
}
}
noteTitleToolTip.Hide(groupNoteInput);
}
I'm not quite sure why do you need to set cursor position, because you can set tool tip to appear where you tell it, and not necessarily where the mouse is.
For example:
tooltip1.Show("My tip", controlOnWhichToShow, 15, 15);
would display the tip at upper left corner of the controlOnWhichToShow, 15 points away from edges.
If I misunderstood you, than please specify at which point in time is the mouse position being used.
If you sync the MouseHover event, you can create the Tooltip as veljkoz describes. In this way you can place the tooltip as you like. The code would look smething like this:
protected override void OnMouseHover(EventArgs e)
{
ToolTip myToolTip = new ToolTip();
myToolTip.IsBalloon = true;
// TODO The x and y coordinates should be what ever you wish.
myToolTip.Show("Helpful Text Also", this, 50, 50);
base.OnMouseHover(e);
}
Hope that helps.
In Windows Forms the mouse is captured by the control when the user presses a mouse button on a control, and the mouse is released by the control when the user releases the mouse button.
The Capture property of the Control class specifies whether a control has captured the mouse. To determine when a control loses mouse capture, handle the MouseCaptureChanged event.
Only the foreground window can capture the mouse. When a background window attempts to capture the mouse, the window receives messages only for mouse events that occur when the mouse pointer is within the visible portion of the window. Also, even if the foreground window has captured the mouse, the user can still click another window, bringing it to the foreground. When the mouse is captured, shortcut keys do not work.
More here. Mouse Capture in Windows Forms
You can do what you say with a Class. You can do it in a very simple way.
one create class and
namespace MousLokasyonbulma
{
class benimtooltip : ToolTip
{
[System.Runtime.InteropServices.DllImport("User32.dll")]
static extern bool MoveWindow(IntPtr h, int x, int y, int width, int height, bool redraw);
public benimtooltip()
{
this.OwnerDraw = true;
this.Draw += Benimtooltip_Draw;
}
private void Benimtooltip_Draw(object sender, DrawToolTipEventArgs e)
{
e.DrawBackground();
e.DrawBorder();
e.DrawText();
var t = (ToolTip)sender;
var h = t.GetType().GetProperty("Handle",
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
var handle = (IntPtr)h.GetValue(t);
var location = new Point(650, 650);
var ss= MoveWindow(handle, location.X, location.Y, e.Bounds.Width, e.Bounds.Height, false);
}
}
}
full Code MyGithup
Example Project image
https://i.hizliresim.com/1pndZG.png
https://i.hizliresim.com/Lvo3Rb.png

Categories