简体   繁体   English

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

[英].NET WPF Remember window size between sessions

Basically when user resizes my application's window I want application to be same size when application is re-opened again.基本上,当用户调整我的应用程序窗口的大小时,我希望应用程序再次重新打开时应用程序的大小相同。

At first I though of handling SizeChanged event and save Height and Width, but I think there must be easier solution.起初我虽然处理 SizeChanged 事件并保存高度和宽度,但我认为必须有更简单的解决方案。

Pretty simple problem, but I can not find easy solution to it.很简单的问题,但我找不到简单的解决方案。

Save the values in the user.config file.将值保存在 user.config 文件中。

You'll need to create the value in the settings file - it should be in the Properties folder.您需要在设置文件中创建值 - 它应该在 Properties 文件夹中。 Create five values:创建五个值:

  • Top of type double Top类型double
  • Left of type double double类型的Left
  • Height of type double double Height
  • Width of type double double类型的Width
  • Maximized of type bool - to hold whether the window is maximized or not. Maximized类型bool - 保存窗口是否最大化。 If you want to store more information then a different type or structure will be needed.如果要存储更多信息,则需要不同的类型或结构。

Initialise the first two to 0 and the second two to the default size of your application, and the last one to false.将前两个初始化为 0,将后两个初始化为应用程序的默认大小,最后一个为 false。

Create a Window_OnSourceInitialized event handler and add the following:创建一个 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;
}

NOTE: The set window placement needs to go in the on source initialised event of the window not the constructor, otherwise if you have the window maximised on a second monitor, it will always restart maximised on the primary monitor and you won't be able to access it.注意:设置窗口放置需要进入窗口的源初始化事件而不是构造函数,否则如果您在第二台显示器上最大化窗口,它将始终在主显示器上重新启动最大化并且您将无法访问它。

Create a Window_Closing event handler and add the following:创建一个 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();

This will fail if the user makes the display area smaller - either by disconnecting a screen or changing the screen resolution - while the application is closed so you should add a check that the desired location and size is still valid before applying the values.如果用户在应用程序关闭时使显示区域变小(通过断开屏幕连接或更改屏幕分辨率),这将失败,因此您应该在应用值之前添加检查所需的位置和大小仍然有效。

Actually you don't need to use code-behind to do that (except for saving the settings).实际上,您不需要使用代码隐藏来执行此操作(保存设置除外)。 You can use a custom markup extension to bind the window size and position to the settings like this :您可以使用自定义标记扩展将窗口大小和位置绑定到如下设置:

<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}">

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

Just wrote a blog entry detailing how to do this in a simple and robust manner. 刚刚写了一篇博客文章,详细介绍了如何以简单而强大的方式执行此操作。 It uses the GetWindowPlacement and SetWindowPlacement functions mentioned by Andy, but with some of the odd behavior he mentioned cleaned up: 它使用了Andy提到的GetWindowPlacementSetWindowPlacement函数,但是他提到的一些奇怪行为得以清除:

https://engy.us/blog/2010/03/08/saving-window-size-and-location-in-wpf-and-winforms/ https://engy.us/blog/2010/03/08/saving-window-size-and-location-in-wpf-and-winforms/

Here are some differences with other answers: 以下是与其他答案的一些区别:

  • This code is aware of monitor changes, so it knows to move a window when a monitor is disconnected (so it's not stranded off screen). 此代码知道监视器的更改,因此它知道在监视器断开连接时移动窗口(因此它不会滞留在屏幕之外)。
  • It can also save the normal size of the window even when it's maximized, so it knows what to restore to when un-maximized. 即使最大化,它也可以保存窗口的正常大小,因此它知道在未最大化时恢复到什么大小。
  • It also consolidates the window state into a single string, which is easier to persist. 它还将窗口状态合并为单个字符串,这样更易​​于持久。
  • Code defines a class that can be used on one or more windows in your project by just hooking two events. 代码定义了一个类,只需挂钩两个事件即可在项目中的一个或多个窗口上使用。 Thus, it does not clutter any of your project files. 因此,它不会使您的任何项目文件混乱。

While you can "roll your own" and manually save the settings somewhere, and in general it will work, it is very easy to not handle all of the cases correctly.虽然您可以“自己动手”并手动将设置保存在某处,并且通常会起作用,但很容易无法正确处理所有情况。 It is much better to let the OS do the work for you, by calling GetWindowPlacement() at exit and SetWindowPlacement() at startup.通过在退出时调用GetWindowPlacement()和在启动时调用SetWindowPlacement() ,让操作系统为您完成工作要好得多。 It handles all of the crazy edge cases that can occur (multiple monitors, save the normal size of the window if it is closed while maximized, etc.) so that you don't have to.它处理所有可能发生的疯狂边缘情况(多显示器,如果窗口在最大化时关闭,则保存窗口的正常大小等),因此您不必这样做。

This MSDN Sample shows how to use these with a WPF app.此 MSDN 示例展示了如何将这些与 WPF 应用程序一起使用。 The sample isn't perfect (the window will start in the upper left corner as small as possible on first run, and there is some odd behavior with the Settings designer saving a value of type WINDOWPLACEMENT ), but it should at least get you started.该示例并不完美(第一次运行时,窗口将从左上角开始,尽可能小,并且设置设计器保存了WINDOWPLACEMENT类型的值有一些奇怪的行为),但它至少应该让你开始.

The "long form" binding that Thomas posted above requires almost no coding, just make sure you have the namespace binding: 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}">

Then to save on the code-behind:然后保存代码隐藏:

private void frmMain_Closed(object sender, EventArgs e)
{
    Properties.Settings.Default.Save();
}

Alternatively, you might like the following approach too ( see source ).或者,您可能也喜欢以下方法( 请参阅源代码)。 Add the WindowSettings class to your project and insert WindowSettings.Save="True" in your main window's header:将 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">

Where WindowSettings is defined as follows:其中 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
}
}

This is the most elegant solution that I've seen over the internet. 这是我在互联网上看到的最优雅的解决方案。 Check this out: 看一下这个:

http://blogs.msdn.com/b/davidrickard/archive/2010/03/09/saving-window-size-and-location-in-wpf-and-winforms.aspx http://blogs.msdn.com/b/davidrickard/archive/2010/03/09/saving-window-size-and-location-in-wpf-and-winforms.aspx

It works for WPF and for WinForms too. 它也适用于WPF和WinForms。

The default way of solving it is to use settings files.解决它的默认方法是使用设置文件。 The problem with settings files is that you have to define all the settings and write the code that copies data back and forth yourself.设置文件的问题在于您必须定义所有设置并自己编写来回复制数据的代码。 Quite tedious if you have a lot of properties to keep track of.如果您要跟踪很多属性,则非常乏味。

I made a pretty flexible and very easy to use library for this, you just tell it which properties of which object to track and it does the rest.我为此创建了一个非常灵活且易于使用的库,您只需告诉它要跟踪哪个对象的哪些属性,剩下的就交给它了。 You can configure the crap out of it too if you like.如果你愿意,你也可以配置它。

The library is called Jot (github) , here is an old CodeProject article I wrote about it.该库名为Jot (github) ,这是我写的一篇关于它的旧CodeProject 文章

Here's how you'd use it to keep track of a window's size and location:以下是如何使用它来跟踪窗口的大小和位置:

public MainWindow()
{
    InitializeComponent();

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

Jot vs. settings files: With Jot there's considerably less code, and it's a lot less error prone since you only need to mention each property once . Jot 与设置文件:使用 Jot 的代码要少得多,而且不容易出错,因为您只需要提及每个属性一次 With settings files you need to mention each property 5 times : once when you explicitly create the property and an additional four times in the code that copies the values back and forth.对于设置文件,您需要提及每个属性5 次:一次是在您明确创建属性时,另外四次是在来回复制值的代码中。

Storage, serialization etc are completely configurable.存储、序列化等是完全可配置的。 Also, when using IOC, you can even hook it up so that it applies tracking automatically to all objects it resolves so that all you need to do to make a property persistent is slap a [Trackable] attribute on it.此外,在使用 IOC 时,您甚至可以将其连接起来,以便它自动将跟踪应用于它解析的所有对象,以便使属性持久化所需要做的就是在其上添加 [Trackable] 属性。

I'm writing all this because I think the library is top notch and I want to mouth off about it.我写这一切是因为我认为图书馆是一流的,我想说出来。

I wrote a quick class which does this.我写了一个快速课程来做到这一点。 Here is how it's called:这是它的名字:

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

And here is the code:这是代码:

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();
        }
    }
}

There's a NuGet Project RestoreWindowPlace see on github that does all this for you, saving the information in an XML file.github上有一个NuGet 项目 RestoreWindowPlace可以为您完成所有这些,将信息保存在 XML 文件中。

To get it to work on a window, it's as simple as calling:要让它在窗口上工作,就像调用一样简单:

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

In App you create the class that manages your windows.在 App 中,您创建管理窗口的类。 See the github link above for more information.有关更多信息,请参阅上面的 github 链接。

I made a more generic solution based on RandomEngys brilliant answer.我根据 RandomEngys 出色的答案制作了一个更通用的解决方案。 It saves the position to file in the running folder and you don't need to create new properties for each new window you create.它将位置保存到正在运行的文件夹中的文件中,您无需为您创建的每个新窗口创建新属性。 This sollution works great for me with minimal code in code behind.这个解决方案对我来说非常有用,代码后面的代码最少。

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);

    }
}

In your code behind you add these two methods在你后面的代码中添加这两个方法

///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();
}

in the xaml window you add this在 xaml 窗口中添加这个

Closing="ClosingTrigger"

Create a string named WindowXml in your default Settings.在默认设置中创建一个名为 WindowXml 的字符串。

Use this extension method on your Window Loaded and Closing events to restore and save Window size and location.在窗口加载和关闭事件上使用此扩展方法来恢复和保存窗口大小和位置。

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);
        }
    }
}

I'm using the answer from Lance Cleveland and bind the Setting.我正在使用 Lance Cleveland 的答案并绑定设置。 But i'm using some more Code to avoid that my Window is out of Screen.但是我正在使用更多代码来避免我的窗口超出屏幕。

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;
}

You might like this:你可能喜欢这个:

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());
        }
    }
}

When the app closes:当应用程序关闭时:

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

When the app starts:当应用程序启动时:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM