.NET WPF 记住会话之间的窗口大小

Posted

技术标签:

【中文标题】.NET WPF 记住会话之间的窗口大小【英文标题】:.NET WPF Remember window size between sessions 【发布时间】:2010-10-25 06:25:43 【问题描述】:

基本上,当用户调整我的应用程序窗口的大小时,我希望应用程序在再次重新打开时保持相同的大小。

起初我想处理 SizeChanged 事件并保存高度和宽度,但我认为必须有更简单的解决方案。

很简单的问题,但我找不到简单的解决方案。

【问题讨论】:

请注意,如果您要重新调整大小和 位置(就像下面的大多数代码示例所做的那样),您将需要处理某人的边缘情况拔下上次显示窗口的显示器,以避免将窗口显示在屏幕外。 @OmerRaviv 您是否找到了考虑边缘情况的示例? 我的声誉太低,无法添加评论,因此我创建了这个新的遮阳篷。我使用与 Lance Cleveland 相同的解决方案,包括 RobJohnson 的设置,但如果您将其用于子窗口并希望在同时... 【参考方案1】:

将值保存在 user.config 文件中。

您需要在设置文件中创建值 - 它应该在属性文件夹中。创建五个值:

Top 类型为 double Left 类型为 double Height 类型为 double Width 类型为 double Maximized 类型为 bool - 保存窗口是否最大化。如果您想存储更多信息,则需要不同的类型或结构。

将前两个初始化为 0,后两个初始化为应用程序的默认大小,最后一个初始化为 false。

创建一个 Window_OnSourceInitialized 事件处理程序并添加以下内容:

this.Top = Properties.Settings.Default.Top;
this.Left = Properties.Settings.Default.Left;
this.Height = Properties.Settings.Default.Height;
this.Width = Properties.Settings.Default.Width;
// Very quick and dirty - but it does the job
if (Properties.Settings.Default.Maximized)

    WindowState = WindowState.Maximized;

注意:设置的窗口位置需要进入窗口的源初始化事件而不是构造函数,否则如果您在第二个监视器上最大化窗口,它将始终重新启动最大化主显示器,您将无法访问它。

创建一个 Window_Closing 事件处理程序并添加以下内容:

if (WindowState == WindowState.Maximized)

    // Use the RestoreBounds as the current values will be 0, 0 and the size of the screen
    Properties.Settings.Default.Top = RestoreBounds.Top;
    Properties.Settings.Default.Left = RestoreBounds.Left;
    Properties.Settings.Default.Height = RestoreBounds.Height;
    Properties.Settings.Default.Width = RestoreBounds.Width;
    Properties.Settings.Default.Maximized = true;

else

    Properties.Settings.Default.Top = this.Top;
    Properties.Settings.Default.Left = this.Left;
    Properties.Settings.Default.Height = this.Height;
    Properties.Settings.Default.Width = this.Width;
    Properties.Settings.Default.Maximized = false;


Properties.Settings.Default.Save();

如果用户在应用程序关闭时缩小显示区域(通过断开屏幕连接或更改屏幕分辨率),这将失败,因此您应该在应用值之前添加检查所需位置和大小是否仍然有效.

【讨论】:

实际上,范围为“User”的设置并没有保存在Program Files的app.config文件中,而是保存在用户应用程序数据目录下的user.config文件中。所以这不是问题...... 其实你可以在设置中添加“WindowState”。选择类型 -> 浏览 -> PresentationFramework -> System.Windows -> WindowState :) FWIW,我也从大小更改的处理程序中执行此操作,以防应用程序崩溃。它们在未处理的异常处理中很少见,但是当它们神秘地发生时,为什么要以丢失的大小/位置来惩罚用户呢。 这段代码有一个错误,如果用户在他/她的第二个屏幕上打开窗口,然后断开该屏幕与计算机的连接,下次他们打开窗口时,它将显示离屏。如果窗口是模态的,用户将根本无法与应用程序交互,也无法理解发生了什么。您需要在converting the screen coordinates to DPI dependant values. 之后使用 Window.GetScreen() 添加边界检查 @OmerRaviv - 这不是错误,而是限制 :) 说真的 - 我没有解决问题的这方面。【参考方案2】:

实际上,您不需要使用代码隐藏来执行此操作(保存设置除外)。您可以使用自定义标记扩展将窗口大小和位置绑定到如下设置:

<Window x:Class="WpfApplication1.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:my="clr-namespace:WpfApplication1"
        Title="Window1"
        Height="my:SettingBinding Height"
        Width="my:SettingBinding Width"
        Left="my:SettingBinding Left"
        Top="my:SettingBinding Top">

您可以在此处找到此标记扩展的代码: http://www.thomaslevesque.com/2008/11/18/wpf-binding-to-application-settings-using-a-markup-extension/

【讨论】:

我更喜欢这个答案,而不是所选接受的答案。干得好。 +1 - 我喜欢使用绑定和扩展!如果将 WindowState 添加到绑定设置中,它会提供完整的功能。或者,如果您在 DataContext 中有可用的用户设置,则可以使用 Binding Settings.Height 等内容。 当窗口最大化时用户关闭应用程序时,这种方法会出现问题。 @Vinicius,你能详细说明一下吗?究竟是什么问题? 如果人们有两个显示器,因此可能有负坐标,然后他们更改了显示器配置并且这些值不再有效?【参考方案3】:

虽然您可以“自己动手”并在某处手动保存设置,而且通常它会起作用,但很容易无法正确处理所有情况。通过在退出时调用GetWindowPlacement() 并在启动时调用SetWindowPlacement(),让操作系统为您完成工作会更好。它处理所有可能发生的疯狂边缘情况(多个监视器,如果窗口在最大化时关闭,则保存窗口的正常大小等),因此您不必这样做。

This MSDN Sample 展示了如何将这些与 WPF 应用程序一起使用。该示例并不完美(第一次运行时,窗口将从左上角开始尽可能小,设置设计器保存WINDOWPLACEMENT 类型的值时会出现一些奇怪的行为),但它至少应该得到你开始了。

【讨论】:

不错的解决方案。但是我刚刚发现 GetWindowPlacement/SetWindowPlacement 是 not Aero Snap aware @RandomEngy 基于此发布了改进的答案。【参考方案4】:

Thomas 上面发布的“长格式”绑定几乎不需要编码,只需确保您有命名空间绑定:

<Window x:Class="WpfApplication1.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:p="clr-namespace:WpfApplication1.Properties"
        Title="Window1"
        Height="Binding Source=x:Static p:Settings.Default, Path=Height, Mode=TwoWay"
        Width="Binding Source=x:Static p:Settings.Default, Path=Width, Mode=TwoWay"
        Left="Binding Source=x:Static p:Settings.Default, Path=Left, Mode=TwoWay"
        Top="Binding Source=x:Static p:Settings.Default, Path=Top, Mode=TwoWay">

然后保存代码隐藏:

private void frmMain_Closed(object sender, EventArgs e)

    Properties.Settings.Default.Save();

【讨论】:

我选择了这个解决方案,但只有在窗口状态正常的情况下才保存设置,否则可能会很麻烦地退出最大化模式 +1 我也用过这个,@DavidSykes - 为窗口状态添加另一个设置似乎效果很好,例如WindowState="Binding Source=x:Static properties:Settings.Default, Path=WindowState, Mode=TwoWay" @RobJohnson 我试过你的建议,效果很好,谢谢。 我节省了Settings.Default.PropertyChanged += (s, e) =&gt; Settings.Default.Save(),并添加了一些延迟WindowState="Binding MainWindow_WindowState, Source=x:Static properties:Settings.Default, Delay=250, Mode=TwoWay",因此它节省了一些SSD。【参考方案5】:

或者,您可能也喜欢以下方法 (see source)。将 WindowSettings 类添加到您的项目中,并在主窗口的标题中插入 WindowSettings.Save="True"

<Window x:Class="YOURPROJECT.Views.ShellView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:Services="clr-namespace:YOURNAMESPACE.Services" 
    Services:WindowSettings.Save="True">

其中WindowSettings定义如下:

using System;
using System.ComponentModel;
using System.Configuration;
using System.Windows;

namespace YOURNAMESPACE.Services

/// <summary>
///   Persists a Window's Size, Location and WindowState to UserScopeSettings
/// </summary>
public class WindowSettings

    #region Fields

    /// <summary>
    ///   Register the "Save" attached property and the "OnSaveInvalidated" callback
    /// </summary>
    public static readonly DependencyProperty SaveProperty = DependencyProperty.RegisterAttached("Save", typeof (bool), typeof (WindowSettings), new FrameworkPropertyMetadata(OnSaveInvalidated));

    private readonly Window mWindow;

    private WindowApplicationSettings mWindowApplicationSettings;

    #endregion Fields

    #region Constructors

    public WindowSettings(Window pWindow)  mWindow = pWindow; 

    #endregion Constructors

    #region Properties

    [Browsable(false)] public WindowApplicationSettings Settings 
        get 
            if (mWindowApplicationSettings == null) mWindowApplicationSettings = CreateWindowApplicationSettingsInstance();
            return mWindowApplicationSettings;
        
    

    #endregion Properties

    #region Methods

    public static void SetSave(DependencyObject pDependencyObject, bool pEnabled)  pDependencyObject.SetValue(SaveProperty, pEnabled); 

    protected virtual WindowApplicationSettings CreateWindowApplicationSettingsInstance()  return new WindowApplicationSettings(this); 

    /// <summary>
    ///   Load the Window Size Location and State from the settings object
    /// </summary>
    protected virtual void LoadWindowState() 
        Settings.Reload();
        if (Settings.Location != Rect.Empty) 
            mWindow.Left = Settings.Location.Left;
            mWindow.Top = Settings.Location.Top;
            mWindow.Width = Settings.Location.Width;
            mWindow.Height = Settings.Location.Height;
        
        if (Settings.WindowState != WindowState.Maximized) mWindow.WindowState = Settings.WindowState;
    

    /// <summary>
    ///   Save the Window Size, Location and State to the settings object
    /// </summary>
    protected virtual void SaveWindowState() 
        Settings.WindowState = mWindow.WindowState;
        Settings.Location = mWindow.RestoreBounds;
        Settings.Save();
    

    /// <summary>
    ///   Called when Save is changed on an object.
    /// </summary>
    private static void OnSaveInvalidated(DependencyObject pDependencyObject, DependencyPropertyChangedEventArgs pDependencyPropertyChangedEventArgs) 
        var window = pDependencyObject as Window;
        if (window != null)
            if ((bool) pDependencyPropertyChangedEventArgs.NewValue) 
                var settings = new WindowSettings(window);
                settings.Attach();
            
    

    private void Attach() 
        if (mWindow != null) 
            mWindow.Closing += WindowClosing;
            mWindow.Initialized += WindowInitialized;
            mWindow.Loaded += WindowLoaded;
        
    

    private void WindowClosing(object pSender, CancelEventArgs pCancelEventArgs)  SaveWindowState(); 

    private void WindowInitialized(object pSender, EventArgs pEventArgs)  LoadWindowState(); 

    private void WindowLoaded(object pSender, RoutedEventArgs pRoutedEventArgs)  if (Settings.WindowState == WindowState.Maximized) mWindow.WindowState = Settings.WindowState; 

    #endregion Methods

    #region Nested Types

    public class WindowApplicationSettings : ApplicationSettingsBase
    
        #region Constructors

        public WindowApplicationSettings(WindowSettings pWindowSettings)  

        #endregion Constructors

        #region Properties

        [UserScopedSetting] public Rect Location 
            get 
                if (this["Location"] != null) return ((Rect) this["Location"]);
                return Rect.Empty;
            
            set  this["Location"] = value; 
        

        [UserScopedSetting] public WindowState WindowState 
            get 
                if (this["WindowState"] != null) return (WindowState) this["WindowState"];
                return WindowState.Normal;
            
            set  this["WindowState"] = value; 
        

        #endregion Properties
    

    #endregion Nested Types


【讨论】:

【参考方案6】:

有一个NuGet Project RestoreWindowPlace 请参阅github,它会为您完成所有这些工作,并将信息保存在 XML 文件中。

要让它在窗口上工作,就像调用一样简单:

((App)Application.Current).WindowPlace.Register(this);

在 App 中,您创建管理窗口的类。有关详细信息,请参阅上面的 github 链接。

【讨论】:

这个包很好用,它还能正确处理最小化的窗口和断开连接的显示器。恕我直言,这应该是公认的答案。【参考方案7】:

默认的解决方法是使用设置文件。设置文件的问题在于您必须自己定义所有设置并编写来回复制数据的代码。如果您有很多属性要跟踪,那就太乏味了。

我为此制作了一个非常灵活且非常易于使用的库,您只需告诉它要跟踪哪个对象的哪些属性,剩下的就交给它了。如果你愿意,你也可以配置它的废话。

这个库叫做Jot (github),这里是我写的一个旧的CodeProject article。

以下是您如何使用它来跟踪窗口的大小和位置:

public MainWindow()

    InitializeComponent();

    _stateTracker.Configure(this)
        .IdentifyAs("MyMainWindow")
        .AddProperties(nameof(Height), nameof(Width), nameof(Left), nameof(Top), nameof(WindowState))
        .RegisterPersistTrigger(nameof(Closed))
        .Apply();

Jot 与设置文件:使用 Jot 的代码要少得多,而且由于您只需要提及每个属性一次,因此更不容易出错。 对于设置文件,您需要提及每个属性 5 次:一次是在您明确创建属性时,另外四次是在代码中来回复制值。

存储、序列化等是完全可配置的。此外,在使用 IOC 时,您甚至可以将其连接起来,以便它自动将跟踪应用于它解析的所有对象,这样您只需在其上添加一个 [Trackable] 属性即可使属性持久化。

我写这一切是因为我认为图书馆是一流的,我想把它说出来。

【讨论】:

很好,谢谢 - 我在一个新类中使用了你的代码 sn-p 来设置状态跟踪器,其路径基于程序的名称。从现在开始我只需要写一行就可以处理所有的窗口属性【参考方案8】:

我根据 RandomEngys 的出色回答做了一个更通用的解决方案。它将位置保存到运行文件夹中的文件中,您无需为创建的每个新窗口创建新属性。这个解决方案对我来说非常有用,代码中的代码最少。

using System;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows;
using System.Windows.Interop;
using System.Xml;
using System.Xml.Serialization;

namespace WindowPlacementNameSpace


    // RECT structure required by WINDOWPLACEMENT structure
    [Serializable]
    [StructLayout(LayoutKind.Sequential)]
    public struct RECT
    
        public int Left;
        public int Top;
        public int Right;
        public int Bottom;

        public RECT(int left, int top, int right, int bottom)
        
            this.Left = left;
            this.Top = top;
            this.Right = right;
            this.Bottom = bottom;
        
    

    // POINT structure required by WINDOWPLACEMENT structure
    [Serializable]
    [StructLayout(LayoutKind.Sequential)]
    public struct POINT
    
        public int X;
        public int Y;

        public POINT(int x, int y)
        
            this.X = x;
            this.Y = y;
        
    

    // WINDOWPLACEMENT stores the position, size, and state of a window
    [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;
    

    public static class WindowPlacement
    
        private static readonly Encoding Encoding = new UTF8Encoding();
        private static readonly XmlSerializer Serializer = new XmlSerializer(typeof(WINDOWPLACEMENT));

        [DllImport("user32.dll")]
        private static extern bool SetWindowPlacement(IntPtr hWnd, [In] ref WINDOWPLACEMENT lpwndpl);

        [DllImport("user32.dll")]
        private static extern bool GetWindowPlacement(IntPtr hWnd, out WINDOWPLACEMENT lpwndpl);

        private const int SW_SHOWNORMAL = 1;
        private const int SW_SHOWMINIMIZED = 2;

        private static void SetPlacement(IntPtr windowHandle, string placementXml)
        
            if (string.IsNullOrEmpty(placementXml))
            
                return;
            

            byte[] xmlBytes = Encoding.GetBytes(placementXml);

            try
            
                WINDOWPLACEMENT placement;
                using (MemoryStream memoryStream = new MemoryStream(xmlBytes))
                
                    placement = (WINDOWPLACEMENT)Serializer.Deserialize(memoryStream);
                

                placement.length = Marshal.SizeOf(typeof(WINDOWPLACEMENT));
                placement.flags = 0;
                placement.showCmd = (placement.showCmd == SW_SHOWMINIMIZED ? SW_SHOWNORMAL : placement.showCmd);
                SetWindowPlacement(windowHandle, ref placement);
            
            catch (InvalidOperationException)
            
                // Parsing placement XML failed. Fail silently.
            
        

        private static string GetPlacement(IntPtr windowHandle)
        
            WINDOWPLACEMENT placement;
            GetWindowPlacement(windowHandle, out placement);

            using (MemoryStream memoryStream = new MemoryStream())
            
                using (XmlTextWriter xmlTextWriter = new XmlTextWriter(memoryStream, Encoding.UTF8))
                
                    Serializer.Serialize(xmlTextWriter, placement);
                    byte[] xmlBytes = memoryStream.ToArray();
                    return Encoding.GetString(xmlBytes);
                
            
        
        public static void ApplyPlacement(this Window window)
        
            var className = window.GetType().Name;
            try
            
                var pos = File.ReadAllText(Directory + "\\" + className + ".pos");
                SetPlacement(new WindowInteropHelper(window).Handle, pos);
            
            catch (Exception exception)
            
                Log.Error("Couldn't read position for " + className, exception);
            

        

        public static void SavePlacement(this Window window)
        
            var className = window.GetType().Name;
            var pos =  GetPlacement(new WindowInteropHelper(window).Handle);
            try
            
                File.WriteAllText(Directory + "\\" + className + ".pos", pos);
            
            catch (Exception exception)
            
                Log.Error("Couldn't write position for " + className, exception);
            
        
        private static string Directory => Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);

    

在你后面的代码中添加这两个方法

///This method is save the actual position of the window to file "WindowName.pos"
private void ClosingTrigger(object sender, EventArgs e)

    this.SavePlacement();

///This method is load the actual position of the window from the file
protected override void OnSourceInitialized(EventArgs e)

    base.OnSourceInitialized(e);
    this.ApplyPlacement();

在 xaml 窗口中添加这个

Closing="ClosingTrigger"

【讨论】:

【参考方案9】:

我写了一个快速课程来做到这一点。这是它的名称:

    public MainWindow()
    
        FormSizeSaver.RegisterForm(this, () => Settings.Default.MainWindowSettings,
                                   s =>
                                   
                                       Settings.Default.MainWindowSettings = s;
                                       Settings.Default.Save();
                                   );
        InitializeComponent();
        ...

这里是代码:

public class FormSizeSaver

    private readonly Window window;
    private readonly Func<FormSizeSaverSettings> getSetting;
    private readonly Action<FormSizeSaverSettings> saveSetting;
    private FormSizeSaver(Window window, Func<string> getSetting, Action<string> saveSetting)
    
        this.window = window;
        this.getSetting = () => FormSizeSaverSettings.FromString(getSetting());
        this.saveSetting = s => saveSetting(s.ToString());

        window.Initialized += InitializedHandler;
        window.StateChanged += StateChangedHandler;
        window.SizeChanged += SizeChangedHandler;
        window.LocationChanged += LocationChangedHandler;
    

    public static FormSizeSaver RegisterForm(Window window, Func<string> getSetting, Action<string> saveSetting)
    
        return new FormSizeSaver(window, getSetting, saveSetting);
    


    private void SizeChangedHandler(object sender, SizeChangedEventArgs e)
    
        var s = getSetting();
        s.Height = e.NewSize.Height;
        s.Width = e.NewSize.Width;
        saveSetting(s);
    

    private void StateChangedHandler(object sender, EventArgs e)
    
        var s = getSetting();
        if (window.WindowState == WindowState.Maximized)
        
            if (!s.Maximized)
            
                s.Maximized = true;
                saveSetting(s);
            
        
        else if (window.WindowState == WindowState.Normal)
        
            if (s.Maximized)
            
                s.Maximized = false;
                saveSetting(s);
            
        
    

    private void InitializedHandler(object sender, EventArgs e)
    
        var s = getSetting();
        window.WindowState = s.Maximized ? WindowState.Maximized : WindowState.Normal;

        if (s.Height != 0 && s.Width != 0)
        
            window.Height = s.Height;
            window.Width = s.Width;
            window.WindowStartupLocation = WindowStartupLocation.Manual;
            window.Left = s.XLoc;
            window.Top = s.YLoc;
        
    

    private void LocationChangedHandler(object sender, EventArgs e)
    
        var s = getSetting();
        s.XLoc = window.Left;
        s.YLoc = window.Top;
        saveSetting(s);
    


[Serializable]
internal class FormSizeSaverSettings

    public double Height, Width, YLoc, XLoc;
    public bool Maximized;

    public override string ToString()
    
        using (var ms = new MemoryStream())
        
            var bf = new BinaryFormatter();
            bf.Serialize(ms, this);
            ms.Position = 0;
            byte[] buffer = new byte[(int)ms.Length];
            ms.Read(buffer, 0, buffer.Length);
            return Convert.ToBase64String(buffer);
        
    

    internal static FormSizeSaverSettings FromString(string value)
    
        try
        
            using (var ms = new MemoryStream(Convert.FromBase64String(value)))
            
                var bf = new BinaryFormatter();
                return (FormSizeSaverSettings) bf.Deserialize(ms);
            
        
        catch (Exception)
        
            return new FormSizeSaverSettings();
        
    

【讨论】:

window.Intitialized 应该是 window.Loaded 见mostlytech.blogspot.com/2008/01/… @Gleb,我认为两者都有效。你在初始化时遇到问题了吗? 是的,因为如果您只使用初始化事件,最大化的窗口将在不正确的屏幕上。我所做的,这似乎有效:现在我也订阅了 Loaded 事件。我移动了 _window.WindowState = s.Maximized ? WindowState.Maximized:WindowState.Normal; “加载”事件处理程序内的行。 window.Initialized += InitializedHandler; window.Loaded += LoadedHandler;顺便说一句:我喜欢这种方法【参考方案10】:

你可能会喜欢这个:

public class WindowStateHelper

    public static string ToXml(System.Windows.Window win)
    
        XElement bounds = new XElement("Bounds");
        if (win.WindowState == System.Windows.WindowState.Maximized)
        
            bounds.Add(new XElement("Top", win.RestoreBounds.Top));
            bounds.Add(new XElement("Left", win.RestoreBounds.Left));
            bounds.Add(new XElement("Height", win.RestoreBounds.Height));
            bounds.Add(new XElement("Width", win.RestoreBounds.Width));
        
        else
        
            bounds.Add(new XElement("Top", win.Top));
            bounds.Add(new XElement("Left", win.Left));
            bounds.Add(new XElement("Height", win.Height));
            bounds.Add(new XElement("Width", win.Width));
        
        XElement root = new XElement("WindowState",
            new XElement("State", win.WindowState.ToString()),
            new XElement("Visibility", win.Visibility.ToString()),
            bounds);

        return root.ToString();
    

    public static void FromXml(string xml, System.Windows.Window win)
    
        try
        
            XElement root = XElement.Parse(xml);
            string state = root.Descendants("State").FirstOrDefault().Value;
            win.WindowState = (System.Windows.WindowState)Enum.Parse(typeof(System.Windows.WindowState), state);

            state = root.Descendants("Visibility").FirstOrDefault().Value;
            win.Visibility = (System.Windows.Visibility)Enum.Parse(typeof(System.Windows.Visibility), state);

            XElement bounds = root.Descendants("Bounds").FirstOrDefault();
            win.Top = Convert.ToDouble(bounds.Element("Top").Value);
            win.Left = Convert.ToDouble(bounds.Element("Left").Value);
            win.Height = Convert.ToDouble(bounds.Element("Height").Value);
            win.Width = Convert.ToDouble(bounds.Element("Width").Value);
        
        catch (Exception x)
        
            System.Console.WriteLine(x.ToString());
        
    

应用关闭时:

        Properties.Settings.Default.Win1Placement = WindowStateHelper.ToXml(win1);
        Properties.Settings.Default.Win2Placement = WindowStateHelper.ToXml(win2);
        ...

应用启动时:

        WindowStateHelper.FromXml(Properties.Settings.Default.Win1Placement, win1);
        WindowStateHelper.FromXml(Properties.Settings.Default.Win2Placement, win2);
        ...

【讨论】:

【参考方案11】:

在默认设置中创建一个名为 WindowXml 的字符串。

在您的 Window Loaded 和 Closing 事件上使用此扩展方法来恢复和保存 Window 大小和位置。

using YourProject.Properties;
using System;
using System.Linq;
using System.Windows;
using System.Xml.Linq;

namespace YourProject.Extensions

    public static class WindowExtensions
    
        public static void SaveSizeAndLocation(this Window w)
        
            try
            
                var s = "<W>";
                s += GetNode("Top", w.WindowState == WindowState.Maximized ? w.RestoreBounds.Top : w.Top);
                s += GetNode("Left", w.WindowState == WindowState.Maximized ? w.RestoreBounds.Left : w.Left);
                s += GetNode("Height", w.WindowState == WindowState.Maximized ? w.RestoreBounds.Height : w.Height);
                s += GetNode("Width", w.WindowState == WindowState.Maximized ? w.RestoreBounds.Width : w.Width);
                s += GetNode("WindowState", w.WindowState);
                s += "</W>";

                Settings.Default.WindowXml = s;
                Settings.Default.Save();
            
            catch (Exception)
            
            
        

        public static void RestoreSizeAndLocation(this Window w)
        
            try
            
                var xd = XDocument.Parse(Settings.Default.WindowXml);
                w.WindowState = (WindowState)Enum.Parse(typeof(WindowState), xd.Descendants("WindowState").FirstOrDefault().Value);
                w.Top = Convert.ToDouble(xd.Descendants("Top").FirstOrDefault().Value);
                w.Left = Convert.ToDouble(xd.Descendants("Left").FirstOrDefault().Value);
                w.Height = Convert.ToDouble(xd.Descendants("Height").FirstOrDefault().Value);
                w.Width = Convert.ToDouble(xd.Descendants("Width").FirstOrDefault().Value);
            
            catch (Exception)
            
            
        

        private static string GetNode(string name, object value)
        
            return string.Format("<0>1</0>", name, value);
        
    

【讨论】:

【参考方案12】:

我正在使用 Lance Cleveland 的答案并绑定设置。 但我正在使用更多代码来避免我的窗口超出屏幕。

private void SetWindowSettingsIntoScreenArea()

    // first detect Screen, where we will display the Window
    // second correct bottom and right position
    // then the top and left position.
    // If Size is bigger than current Screen, it's still possible to move and size the Window

    // get the screen to display the window
    var screen = System.Windows.Forms.Screen.FromPoint(new System.Drawing.Point((int)Default.Left, (int)Default.Top));

    // is bottom position out of screen for more than 1/3 Height of Window?
    if (Default.Top + (Default.Height / 3) > screen.WorkingArea.Height)
        Default.Top = screen.WorkingArea.Height - Default.Height;

    // is right position out of screen for more than 1/2 Width of Window?
    if (Default.Left + (Default.Width / 2) > screen.WorkingArea.Width)
        Default.Left = screen.WorkingArea.Width - Default.Width;

    // is top position out of screen?
    if (Default.Top < screen.WorkingArea.Top)
        Default.Top = screen.WorkingArea.Top;

    // is left position out of screen?
    if (Default.Left < screen.WorkingArea.Left)
        Default.Left = screen.WorkingArea.Left;

【讨论】:

以上是关于.NET WPF 记住会话之间的窗口大小的主要内容,如果未能解决你的问题,请参考以下文章

如何在两个窗口之间维护 Wpf 应用程序中的会话? [关闭]

C# wpf 想让控件随着窗口大小变化而变化

使用WPF更改与窗口大小相关的字体大小?

WPF 窗口大小问题

WPF 从下到上调整窗口大小

如果它比 WPF XAML 中的窗口大,如何缩放字体大小以适应窗口?