记住窗口位置、大小和状态 [on Win + Arrow 对齐](使用多个显示器)
Posted
技术标签:
【中文标题】记住窗口位置、大小和状态 [on Win + Arrow 对齐](使用多个显示器)【英文标题】:Remember Window position, size and state [on Win + Arrow alignment] (with multiple monitors) 【发布时间】:2015-03-06 10:54:18 【问题描述】:在我们的项目中,我们保存了窗口大小、位置和最小化/最大化设置,因此我们可以在重新打开窗口时以完全相同的位置和大小打开窗口。使用本文底部的Window-Behavior
-class,所有这些都运行良好。
然而,问题是当我们使用 Win 按钮 + 箭头时;这会将屏幕与屏幕的一侧对齐,但这未正确保存在行为中。相反,它保存了我使用Win +箭头对齐之前屏幕的位置和大小,也就是它再次打开的位置。
我尝试在SaveWindowState
方法中使用Window的Left
、Top
、ActualWidth
和ActualHeight
(注意:这个方法中的AssociatedObject
是Window。)但是Left
和 Top
似乎偏离了大约 20-40 像素,并且使用 ActualWidth
、ActualHeight
和当前屏幕宽度/高度(使用多个显示器时)保存左右也有点痛。
那么,当用户使用Win +箭头对齐窗口然后关闭它时,有什么方法可以在窗口设置中保存正确的位置和大小?
WindowSettingsBehavior
:
using System;
using System.ComponentModel;
using System.Configuration;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interactivity;
using System.Windows.Interop;
namespace NatWa.MidOffice.Behaviors
/// <summary>
/// Persists a Window's Size, Location and WindowState to UserScopeSettings
/// </summary>
public class WindowSettingsBehavior : Behavior<Window>
[DllImport("user32.dll")]
static extern bool SetWindowPlacement(IntPtr hWnd, [In] ref Windowplacement lpwndpl);
[DllImport("user32.dll")]
static extern bool GetWindowPlacement(IntPtr hWnd, out Windowplacement lpwndpl);
// ReSharper disable InconsistentNaming
const int SW_SHOWNORMAL = 1;
const int SW_SHOWMINIMIZED = 2;
// ReSharper restore InconsistentNaming
internal class WindowApplicationSettings : ApplicationSettingsBase
public WindowApplicationSettings(WindowSettingsBehavior windowSettingsBehavior)
: base(windowSettingsBehavior.AssociatedObject.GetType().FullName)
[UserScopedSetting]
public Windowplacement? Placement
get
if (this["Placement"] != null)
return ((Windowplacement)this["Placement"]);
return null;
set
this["Placement"] = value;
/// <summary>
/// Load the Window Size Location and State from the settings object
/// </summary>
private void LoadWindowState()
Settings.Reload();
if (Settings.Placement == null) return;
try
// Load window placement details for previous application session from application settings.
// If window was closed on a monitor that is now disconnected from the computer,
// SetWindowPlacement will place the window onto a visible monitor.
var wp = Settings.Placement.Value;
wp.length = Marshal.SizeOf(typeof(Windowplacement));
wp.flags = 0;
wp.showCmd = (wp.showCmd == SW_SHOWMINIMIZED ? SW_SHOWNORMAL : wp.showCmd);
var hwnd = new WindowInteropHelper(AssociatedObject).Handle;
SetWindowPlacement(hwnd, ref wp);
catch (Exception ex)
Debug.WriteLine("Failed to load window state:\r\n0", ex);
/// <summary>
/// Save the Window Size, Location and State to the settings object
/// </summary>
private void SaveWindowState()
Windowplacement wp;
var hwnd = new WindowInteropHelper(AssociatedObject).Handle;
GetWindowPlacement(hwnd, out wp);
Settings.Placement = wp;
Settings.Save();
protected override void OnAttached()
base.OnAttached();
AssociatedObject.Closing += WindowClosing;
AssociatedObject.SourceInitialized += WindowSourceInitialized;
private void WindowSourceInitialized(object sender, EventArgs e)
LoadWindowState();
private void WindowClosing(object sender, CancelEventArgs e)
SaveWindowState();
AssociatedObject.Closing -= WindowClosing;
AssociatedObject.SourceInitialized -= WindowSourceInitialized;
private WindowApplicationSettings _windowApplicationSettings;
internal virtual WindowApplicationSettings CreateWindowApplicationSettingsInstance()
return new WindowApplicationSettings(this);
[Browsable(false)]
internal WindowApplicationSettings Settings
get return _windowApplicationSettings
?? (_windowApplicationSettings = CreateWindowApplicationSettingsInstance());
#region Save position classes
[Serializable]
[StructLayout(LayoutKind.Sequential)]
public struct Rect
private int _left;
private int _top;
private int _right;
private int _bottom;
public Rect(int left, int top, int right, int bottom)
_left = left;
_top = top;
_right = right;
_bottom = bottom;
public override bool Equals(object obj)
if (!(obj is Rect)) return base.Equals(obj);
var rect = (Rect)obj;
return rect._bottom == _bottom &&
rect._left == _left &&
rect._right == _right &&
rect._top == _top;
public override int GetHashCode()
return _bottom.GetHashCode() ^
_left.GetHashCode() ^
_right.GetHashCode() ^
_top.GetHashCode();
public static bool operator ==(Rect a, Rect b)
return a._bottom == b._bottom &&
a._left == b._left &&
a._right == b._right &&
a._top == b._top;
public static bool operator !=(Rect a, Rect b)
return !(a == b);
public int Left
get return _left;
set _left = value;
public int Top
get return _top;
set _top = value;
public int Right
get return _right;
set _right = value;
public int Bottom
get return _bottom;
set _bottom = value;
[Serializable]
[StructLayout(LayoutKind.Sequential)]
public struct Point
private int _x;
private int _y;
public Point(int x, int y)
_x = x;
_y = y;
public int X
get return _x;
set _x = value;
public int Y
get return _y;
set _y = value;
public override bool Equals(object obj)
if (!(obj is Point)) return base.Equals(obj);
var point = (Point)obj;
return point._x == _x && point._y == _y;
public override int GetHashCode()
return _x.GetHashCode() ^ _y.GetHashCode();
public static bool operator ==(Point a, Point b)
return a._x == b._x && a._y == b._y;
public static bool operator !=(Point a, Point b)
return !(a == b);
[Serializable]
[StructLayout(LayoutKind.Sequential)]
public struct Windowplacement
public int length;
public int flags;
public int showCmd;
public Point minPosition;
public Point maxPosition;
public Rect normalPosition;
#endregion
【问题讨论】:
想知道为什么要使用user32.dll
导入,而不是直接访问窗口实例?
是的,Aero Snap 非常古怪。最大的问题是它没有通知,也没有办法用winapi捕捉,窗口位置不会更新。请注意 Get/SetWindowPlacement 使用的奇怪坐标系,它会减去任务栏。最好完全避免。
【参考方案1】:
您是否尝试过 System.Windows.Window
实例而不是 p/invoke
?
我使用两个简单的方法来保存和使用这个类设置窗口位置,它可以在不同的应用程序、架构、客户端、Windows 操作系统上完美运行,无论是否有 Aero...
void SetWindowPosition()
this.Left = Settings.Default.WindowPositionLeft;
this.Top = Settings.Default.WindowPositionTop;
void SaveWindowPosition()
Settings.Default.WindowPositionTop = this.Top;
Settings.Default.WindowPositionLeft = this.Left;
Settings.Default.Save();
还是我错过了什么?
【讨论】:
"或者我错过了什么?" 老实说,我完全不知道我有/没有尝试过什么。我在 2.5 多年前发布了这个问题。 ;) 目前我什至不再每天使用 .NET C# 进行开发。 我总是忘记检查线程日期:D 那么在你的新项目中玩得开心!以上是关于记住窗口位置、大小和状态 [on Win + Arrow 对齐](使用多个显示器)的主要内容,如果未能解决你的问题,请参考以下文章