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

As soon as the application starts, I want my WPF window to automatically snap to the right edge of the screen. Is there any way to do this? I also want to keep my size. Thus, unlike the snap behavior that occurs when you drag a window to the edge of the screen, causing the window to resize either on part of the screen or on the entire screen, I want my window to simply snap to the edge at a specific location by default, or then dragged by the user to a specific location, without resizing. I still want to keep the user's ability to drag the window from the edge.

Is there something similar already implemented or do I need to create my own behavior pattern? I tried numerous combinations of search keywords, but could not find anything similar to what I was doing. Some of the search queries included disabling the snap or providing the snap, but nothing was described above.

EDIT:

I could not find a ready-made solution, so I wrote my own. This solution is based on BenVlodgi's suggestions, so I thank him for his help. This is a very crude implementation and still requires a lot of polishing and improvement of the code methods, but it works, and it is a good base for those who want to try this. It is incredibly simple and works great with WPF. The only limitation of this implementation is that I have not yet tried to get it to work with two screens, but it is incredibly simple (I just won’t have time for this, and I don’t need this functionality at the moment), So here is the code, and I hope he helps someone:

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 your window has the following:

 MouseDown="Window_MouseDown" MouseUp="Window_MouseUp" WindowStartupLocation="Manual" Loaded="Window_Loaded" 

Also, this doesn’t work very well with native Windows components in the window, such as the top bar, so I turn off the style and create my own (which is actually good for me, since I don’t want windows to style this):

 WindowStyle="None" 
+6
source share
2 answers

There are no API calls that you can make (as far as I saw) to use the Windows snap functions, however you can just get the System.Windows.Forms.Screen.PrimaryScreen.WorkingArea screen and set the Top , Left , Height and Width Properties of your Window respectively.

Change The above sentence requires forms that you probably don't need. I believe the equivalent of WPF is System.Windows.SystemParameters.WorkArea

+4
source

I don't like the polling approach, but it was hard to find a better solution that is still easy in WPF, so I'm going to post my own.

The solution I found is actually quite simple, as it restores the behavior of the DragMove() method of the window, which gives you the ability to reposition the window while you drag it. The following code updates DragMove() , keeping the distance between the upper 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 can implement the snap / sticky window behavior as follows: the window will adhere to the edge of the screen if it is within 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 disadvantage of this approach is that the binding will not work if the window is dragged in the title bar, as this does not OnMouseLeftButtonDown event (which I do not need because my window is borderless). Perhaps this will still help someone.

+3
source

All Articles