Detect misplaced form after restoring top and left from usersettings - c#

I'm storing the last position for each window.
Next time the user opens the window, the last position is restored.
If the user changes his screen (from dual screen to one screen) or just to a smaller resolution, the form is nowhere to be seen...
How can I detect this? I don't like to store user settings, depending on his environment.
Thanks in advance

Let's start from scratch, you want two settings to store the state of the window. Let's call them Location (type Point, default = 0,0) and Size (type Size, default = 0, 0). You want to save them when the window is resized, avoiding storing state if the window is minimized:
protected override void OnResizeEnd(EventArgs e) {
if (this.WindowState != FormWindowState.Minimized) {
Properties.Settings.Default.Location = this.Location;
Properties.Settings.Default.Size = this.Size;
Properties.Settings.Default.Save();
}
base.OnResizeEnd(e);
}
Restore the state in the form's OnLoad method. You'll want to use Screen.FromPoint() to find the screen bounds back. Add extra code to ensure the window doesn't get too large and locates properly when the screen has disappeared:
protected override void OnLoad(EventArgs e) {
if (Properties.Settings.Default.Size != Size.Empty) {
Screen scr = Screen.FromPoint(Properties.Settings.Default.Location);
int width = Math.Min(Properties.Settings.Default.Size.Width, scr.WorkingArea.Width);
int height = Math.Min(Properties.Settings.Default.Size.Height, scr.WorkingArea.Height);
this.Size = new Size(width, height);
if (scr.WorkingArea.Contains(Properties.Settings.Default.Location))
this.Location = Properties.Settings.Default.Location;
else this.Location = new Point(scr.Bounds.Left + (scr.Bounds.Width - width) / 2,
scr.Bounds.Top + (scr.Bounds.Height - height) / 2);
}
base.OnLoad(e);
}

Use the Bounds property from System.Windows.Forms.Screen.PrimaryScreen to see what the bounds of the screen is, compare that to the position/size of your form and compensate where needed.
To get at the bounds of other screens, use the Screen.AllScreens property on the PrimaryScreen property to gain access to other Screen objects representing multiple screens.
For example, this may be as simple as checking that the Location you want to change to is on an available screen:
foreach (var screen in Screen.AllScreens)
{
if (screen.Bounds.Contains(this.Location))
{
return; // on a screen, so don't update location
}
}
// not found on a screen, so assume screen was removed and move to the primary screen
this.Location = Screen.PrimaryScreen.Bounds.Location;
You can, of course, make this more complicated by deciding which screen contains more of the form than any other (based on Bounds) and make a determination that way; but, without more details about exactly what you want, I can't suggest specifics.

Related

How to develop win form application for the system with the resolution of 1080*1920

I want to develop a windows form application where the destination system is having screen resolution of 1080x1920. but my system is not accepting that resolution . even i cant set my winform size to 1080x1920.
can anybody help me out to solve this problem
This works for me. This is the Load event for the form (you can generate this by double clicking the form in the designer). In this event we check the current dimensions of the screen containing the form. Then we set the form size to match. We also move the form's position to 0, 0 so it doesn't clip off the screen.
//the name of this function will be different for you.
//generate this function by double clicking the form in designer.
//the code inside the function is what you're interested in.
private void MainForm_Load(object sender, EventArgs e)
{
Rectangle screenSize = Screen.GetBounds(this); //find out the current screen size
this.Top = 0; //move the form to top
this.Left = 0; //move the form to left
this.Width = screenSize.Width; //set form width to screen width
this.Height = screenSize.Height; //set form height to screen height
}
EDIT: Also, why not just maximize?
this.WindowState = FormWindowState.Maximized;

How to ensure UWP app is always full screen on launch?

Is there a way (either C# or XAML) I can maximize a UWP app window even after I resized and closed it previously on desktop?
I have tried with ApplicationViewWindowingMode.FullScreen but this makes the app go entire full screen and covers the Windows Taskbar too.
You can use another value PreferredLaunchViewSize from ApplicationViewWindowingMode and then set ApplicationView.PreferredLaunchViewSize but the key is to find out what the size is going to be.
Theoretically, you could use a really big number and window would just extend to the max it could be. However, it's probably safer to just calculate the screen dimensions in effective pixels.
So if you just call the following method before InitializeComponent(); on your main Page, it should maximize the window on startup.
private static void MaximizeWindowOnLoad()
{
// Get how big the window can be in epx.
var bounds = ApplicationView.GetForCurrentView().VisibleBounds;
ApplicationView.PreferredLaunchViewSize = new Size(bounds.Width, bounds.Height);
ApplicationView.PreferredLaunchWindowingMode = ApplicationViewWindowingMode.PreferredLaunchViewSize;
}
Note the app somehow remembers these settings even after you uninstalled it. If you ever want to change back to the default behavior (app starts up with the previous window size), simply call ApplicationView.PreferredLaunchWindowingMode = ApplicationViewWindowingMode.Auto; once and remove all the code.
Update
Looks like in the latest Windows 10 build, ApplicationView.GetForCurrentView().VisibleBounds no longer returns the full window size in effective pixels anymore. So we now need a new way to calculate it.
Turns out it's quite straightforward since the DisplayInformation class also gives us the screen resolution as well as the scale factor.
The following is the updated code -
public MainPage()
{
MaximizeWindowOnLoad();
InitializeComponent();
void MaximizeWindowOnLoad()
{
var view = DisplayInformation.GetForCurrentView();
// Get the screen resolution (APIs available from 14393 onward).
var resolution = new Size(view.ScreenWidthInRawPixels, view.ScreenHeightInRawPixels);
// Calculate the screen size in effective pixels.
// Note the height of the Windows Taskbar is ignored here since the app will only be given the maxium available size.
var scale = view.ResolutionScale == ResolutionScale.Invalid ? 1 : view.RawPixelsPerViewPixel;
var bounds = new Size(resolution.Width / scale, resolution.Height / scale);
ApplicationView.PreferredLaunchViewSize = new Size(bounds.Width, bounds.Height);
ApplicationView.PreferredLaunchWindowingMode = ApplicationViewWindowingMode.PreferredLaunchViewSize;
}
}
If you want to MAXIMISE your app on launch you can use the following:
ApplicationView.PreferredLaunchWindowingMode = ApplicationViewWindowingMode.Maximized;
But be sure to put it into the Loaded Event for your Page or it will not work!
I've too few points to comment directly. None of the above resized to a maximized view for me (or the below single-line ApplicationViewWindowingMode.Maximized method), but I have used some of the answers to come up with something that worked for me. It is still very clunky however. The screen size given in 'DisplayInformation' is too big to allow the page to be resized directly to it. Trying to do it didn't work and I had to take 60 off height and width to get it to return 'true', therefore I have the following bit of nonsense which worked, maybe it will help someone else find a better answer. It goes in the page/window loaded event. Nothing else needs to be added elsewhere.
private void Page_Loaded(object sender, RoutedEventArgs e)
{
var view = ApplicationView.GetForCurrentView();
var displayInfo = DisplayInformation.GetForCurrentView();
double x = ActualWidth;
double y = ActualHeight;
bool answer = true;
// Get the screen resolution (APIs available from 14393 onward).
var resolution = new Size(displayInfo.ScreenWidthInRawPixels-60, displayInfo.ScreenHeightInRawPixels-60);
answer = view.TryResizeView(resolution); //This should return true if the resize is successful
if (answer)
{
x = displayInfo.ScreenWidthInRawPixels - 60;
y = displayInfo.ScreenHeightInRawPixels - 60;
}
answer = true;
while (answer == true)
{
x++;
answer = view.TryResizeView(new Size { Width = x, Height = y });
}
x = x - 1;
answer = true;
while (answer == true)
{
y++;
answer = view.TryResizeView(new Size { Width = x, Height = y });
}
Adding the following line to the OnLaunched event under App.xaml.cs did it for me.
ApplicationView.PreferredLaunchWindowingMode = ApplicationViewWindowingMode.FullScreen;
NOTE: Make sure to add it before the following line
Window.Current.Activate();
If you like to go fullscreen at the runtime use the following line.
ApplicationView.GetForCurrentView().TryEnterFullScreenMode();
I have this one liner that works as I expected Justins code to, but for some reason, when using Justins answer, my window would not be maximized... But then I changed something that did make it maximized but I lost all my fluent design such as Acrylic and RevealHighlite...
So I came up with this one liner which keeps all of my fluent design principles happy:
ApplicationView.GetForCurrentView().TryEnterFullScreenMode();
Something to note:
I did try Justins answer, and I am using his method of MaximizeWindowOnLoad() which I have called straight after the initializeComponent();
Full overview:
public class()
{
this.InitializeComponent();
MaximizeWindowOnLoad();
}
private static void MaximizeWindowOnLoad()
{
ApplicationView.GetForCurrentView().TryEnterFullScreenMode();
}

How to automatically snap a WPF window to an edge of the screen while retaining its size?

As the application starts, I'd like my WPF window to automatically snap to the right edge of the screen. Is there a way to do that? I also want to be able to retain its dimensions. So, unlike the snapping behavior that happens when you drag a window to the edge of the screen, causing the window to resize to either a portion of the screen or full screen, I want my window to simply snap to the edge at a certain location by default or if dragged by the user to a specific location afterwards, without resizing. I still want to retain the ability of the user to drag the window away from the edge.
Is there anything like that already implemented or would I have to create my own behavior schema? I tried numerous search keyword combinations, but couldn't find anything similar to what I'm doing. Some of the searches included disabling snapping behavior or providing snapping behavior, but nothing in the way I described above.
EDIT:
I haven't been able to find a ready solution, so I wrote my own. This solution is based on BenVlodgi's suggestions, so I thank him for helping me out. This is a very rough implementation and still requires a lot of polishing and better code techniques, but it works and it's a good base for anyone wanting to try this. It's incredibly simple and works very well with WPF. The only limitation of this implementation is that I haven't tried getting it to work with two screens yet, but it's incredibly simple (I'm just not going to have time for it and I don't need that functionality at this point). So, here's the code and I hope that it helps someone out there:
public partial class MainWindow : Window
{
// Get the working area of the screen. It excludes any dockings or toolbars, which
// is exactly what we want.
private System.Drawing.Rectangle screen =
System.Windows.Forms.Screen.PrimaryScreen.WorkingArea;
// This will be the flag for the automatic positioning.
private bool dragging = false;
// The usual initialization routine
public MainWindow()
{
InitializeComponent();
}
// Wait until window is lodaded, but prior to being rendered to set position. This
// is done because prior to being loaded you'll get NaN for this.Height and 0 for
// this.ActualHeight.
private void Window_Loaded(object sender, RoutedEventArgs e)
{
// Sets the initial position.
SetInitialWindowPosition();
// Sets the monitoring timer loop.
InitializeWindowPositionMonitoring();
}
// Allows the window to be dragged where the are no other controls present.
// PreviewMouseButton could be used, but then you have to do more work to ensure that if
// you're pressing down on a button, it executes its routine if dragging was not performed.
private void Window_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
// Set the dragging flag to true, so that position would not be reset automatically.
if (e.ChangedButton == System.Windows.Input.MouseButton.Left)
{
dragging = true;
this.DragMove();
}
}
// Similar to MouseDown. We're setting dragging flag to false to allow automatic
// positioning.
private void Window_MouseUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
if (e.ChangedButton == System.Windows.Input.MouseButton.Left)
{
dragging = false;
}
}
// Sets the initial position of the window. I made mine static for now, but later it can
// be modified to be whatever the user chooses in the settings.
private void SetInitialWindowPosition()
{
this.Left = screen.Width - this.Width;
this.Top = screen.Height / 2 - this.Height / 2;
}
// Setup the monitoring routine that automatically positions the window based on its location
// relative to the working area.
private void InitializeWindowPositionMonitoring()
{
var timer = new System.Windows.Threading.DispatcherTimer();
timer.Tick += delegate
{
// Check if window is being dragged (assuming that mouse down on any portion of the
// window is connected to dragging). This is a fairly safe assumption and held
// true thus far. Even if you're not performing any dragging, then the position
// doesn't change and nothing gets reset. You can add an extra check to see if
// position has changed, but there is no significant performance gain.
// Correct me if I'm wrong, but this is just O(n) execution, where n is the number of
// ticks the mouse has been held down on that window.
if (!dragging)
{
// Checking the left side of the window.
if (this.Left > screen.Width - this.Width)
{
this.Left = screen.Width - this.Width;
}
else if (this.Left < 0)
{
this.Left = 0;
}
// Checking the top of the window.
if (this.Top > screen.Height - this.Height)
{
this.Top = screen.Height - this.Height;
}
else if (this.Top < 0)
{
this.Top = 0;
}
}
};
// Adjust this based on performance and preference. I set mine to 10 milliseconds.
timer.Interval = new TimeSpan(0, 0, 0, 0, 10);
timer.Start();
}
}
Make sure that your window has the following:
MouseDown="Window_MouseDown"
MouseUp="Window_MouseUp"
WindowStartupLocation="Manual"
Loaded="Window_Loaded"
Also, this doesn't work well with Windows native components of the window, such as the top bar, so I disable the style and create my own (which is actually good for me, since I don't want the windows style for this):
WindowStyle="None"
I don't like the polling approach, but it's been excessively difficult to find a better solution that is still simple in WPF, so I'm gonna post my own.
The solution that I found is actually quite simple, in that it reimplements the behaviour of the DragMove() method of the window, which gives you the option to change the window position while it's being dragged. The following code reimplements DragMove() by storing the distance between the top left corner of the window and the mouse cursor.
public partial class MainWindow : Window
{
// this is the offset of the mouse cursor from the top left corner of the window
private Point offset = new Point();
public MainWindow()
{
InitializeComponent();
}
private void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Point cursorPos = PointToScreen(Mouse.GetPosition(this));
Point windowPos = new Point(this.Left, this.Top);
offset = (Point)(cursorPos - windowPos);
// capturing the mouse here will redirect all events to this window, even if
// the mouse cursor should leave the window area
Mouse.Capture(this, CaptureMode.Element);
}
private void OnMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
Mouse.Capture(null);
}
private void OnMouseMove(object sender, MouseEventArgs e)
{
if (Mouse.Captured == this && Mouse.LeftButton == MouseButtonState.Pressed)
{
Point cursorPos = PointToScreen(Mouse.GetPosition(this));
double newLeft = cursorPos.X - offset.X;
double newTop = cursorPos.Y - offset.Y;
// here you can change the window position and implement
// the snapping behaviour that you need
this.Left = newLeft;
this.Top = newTop;
}
}
}
Now you could implement the snapping / sticky window behaviour like this: The window will stick to the edge of the screen if it's within a range of 25 pixels (plus or minus).
int snappingMargin = 25;
if (Math.Abs(SystemParameters.WorkArea.Left - newLeft) < snappingMargin)
newLeft = SystemParameters.WorkArea.Left;
else if (Math.Abs(newLeft + this.ActualWidth - SystemParameters.WorkArea.Left - SystemParameters.WorkArea.Width) < snappingMargin)
newLeft = SystemParameters.WorkArea.Left + SystemParameters.WorkArea.Width - this.ActualWidth;
if (Math.Abs(SystemParameters.WorkArea.Top - newTop) < snappingMargin)
newTop = SystemParameters.WorkArea.Top;
else if (Math.Abs(newTop + this.ActualHeight - SystemParameters.WorkArea.Top - SystemParameters.WorkArea.Height) < snappingMargin)
newTop = SystemParameters.WorkArea.Top + SystemParameters.WorkArea.Height - this.ActualHeight;
The downside of this approach is that the snapping will not work, if the window is being dragged on the title bar, because that doesn't fire the OnMouseLeftButtonDown event (which I don't need, because my window is borderless). Maybe it will still help someone.
There is no API calls you can make (as far as I've seen) to use the Windows snapping features, however you could just get the System.Windows.Forms.Screen.PrimaryScreen.WorkingArea of the screen and set your Top, Left, Height and Width Properties of your Window accordingly.
Edit: The above suggestion does require Forms, which you probably don't want. I believe the WPF equivalent is System.Windows.SystemParameters.WorkArea

What is the best way to show a WPF window at the mouse location (to the top left of the mouse)?

I have found that this works PART of the time by inheriting the Windows Forms mouse point and subtracting out the height and width of my window to set the left and top (since my window's size is fixed):
MyWindowObjectThatInheritsWindow window = new MyWindowObjectThatInheritsWindow();
System.Windows.Point mouseLocation = GetMousePositionWindowsForms();
window.Left = mouseLocation.X - 300;
window.Top = mouseLocation.Y - 240;
window.Show();
Edit: Here is the code for getting the mouse position...
public System.Windows.Point GetMousePositionWindowsForms()
{
System.Drawing.Point point = System.Windows.Forms.Control.MousePosition;
return new System.Windows.Point(point.X, point.Y);
}
Note that this works by making the bottom right edge of the window touch the top left of your mouse cursor. But this breaks for different screen resolutions, or maybe multiple monitors with different resolutiosn? I haven't fully narrowed it down yet, but I just tried this same code on another PC, and it seems to spawn the window not to the top left of the mouse cursor, but to the bottom left of it, and a good distance past it...
I should probably add that my window sizes to content, width and height, so I can't just use the ActualWidth and ActualHeight properties since they're not available. Perhaps the issue is in getting that sizing right? Is there any way to do that? I know for sure the 300 and 240 is correct according to my main PC with two monitors running 1920x1080 resolutions, as I have calculated the widths and heights of all the objects in my window which I have explicitly sized. Edit: Just tried explicitly setting the height and width to 240/300, to ensure that the window is no longer sized to content, and I still have this issue when subtracting out the actual height and width!
Any ideas?
In the end, this did the trick:
protected override void OnContentRendered(EventArgs e)
{
base.OnContentRendered(e);
MoveBottomRightEdgeOfWindowToMousePosition();
}
private void MoveBottomRightEdgeOfWindowToMousePosition()
{
var transform = PresentationSource.FromVisual(this).CompositionTarget.TransformFromDevice;
var mouse = transform.Transform(GetMousePosition());
Left = mouse.X - ActualWidth;
Top = mouse.Y - ActualHeight;
}
public System.Windows.Point GetMousePosition()
{
System.Drawing.Point point = System.Windows.Forms.Control.MousePosition;
return new System.Windows.Point(point.X, point.Y);
}
Can you not use something like this?:
Point mousePositionInApp = Mouse.GetPosition(Application.Current.MainWindow);
Point mousePositionInScreenCoordinates =
Application.Current.MainWindow.PointToScreen(mousePositionInApp);
I haven't been able to test it, but I think it should work.
UPDATE >>>
You don't have to use the Application.Current.MainWindow as the parameter in these methods... it should still work if you have access to a Button or another UIElement in a handler:
Point mousePositionInApp = Mouse.GetPosition(openButton);
Point mousePositionInScreenCoordinates = openButton.PointToScreen(mousePositionInApp);
Again, I haven't been able to test this, but if that fails as well, then you can find one more method in the How do I get the current mouse screen coordinates in WPF? post.
You can also do this by slightly modifying your initial example and positioning the window before showing it.
MyWindowObjectThatInheritsWindow window = new MyWindowObjectThatInheritsWindow();
var helper = new WindowInteropHelper(window);
var hwndSource = HwndSource.FromHwnd(helper.EnsureHandle());
var transformFromDevice = hwndSource.CompositionTarget.TransformFromDevice;
System.Windows.Point wpfMouseLocation = transformFromDevice.Transform(GetMousePositionWindowsForms());
window.Left = wpfMouseLocation.X - 300;
window.Top = wpfMouseLocation.Y - 240;
window.Show();

Make Buttons/Labels on windows form bigger/move position on maximise?

I am developing a clock Timer. It is working fine however I am having a issue when the form which is normally small to sit in the corner of the screen out of the way is maximised. Is there a way when it is maximised that I can move the location of the Activity (where all the info is enetered) - I can move that start pause and stop/reset button and I can also move the labels which countdown the time and make them a lot bigger on the maximised display. I have two images - background small and background large which are changing fine on the maximise - I used the code below too hook into the size change event - however the commented out is not working - it does not let be hard code the X,Y co-ordinates of where i would like the activity on maximise...is there something I am missing?
Many Thanks - Colly.
private void CountDownTimer_SizeChanged(object sender, EventArgs e)
{
Image Max = new Bitmap(#"C:\Users\colinmck\Desktop\Timer\CountDownTimer\IgnitionTeamLRG.bmp");
Image Min = new Bitmap(#"C:\Users\colinmck\Desktop\Timer\CountDownTimer\IgnitionTeamSML.bmp");
if (WindowState == FormWindowState.Maximized)
{
BackgroundImage = Max;
//Not Working!!!!!!!!!!!!!
//Activity.Location.X = 60;
//Activity.Location.Y = 65;
}
else
{
BackgroundImage = Min;
}
}
Location.X and Location.Y are read only properties. You should try...
Activity.Location = new Point(60, 65);
Perhaps also take a look into the Anchor property for auto resizing of controls: http://www.tutorialized.com/view/tutorial/C-Resizing-controls-with-form-Anchor-property/52689

Categories

Resources