C# 使用多个显示器恢复窗口大小/位置

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/937298/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 03:44:55  来源:igfitidea点击:

Restoring Window Size/Position With Multiple Monitors

c#winformsmultiple-monitorsappsettings

提问by Michael Sorens

Many posts around about restoring a WinForm position and size.

许多关于恢复 WinForm 位置和大小的帖子。

Examples:

例子:

But I have yet to find code to do this with multiple monitors.

但是我还没有找到用多台显示器来做到这一点的代码。

That is, if I close my .NET Winform app with the window on monitor 2, I want it to save the windows size, location, and state to the application settings, so it could later restore to monitor 2 when I restart the app. It would be nice if, like in the codeproject example above, it includes some sanity checks, as in if the saved location is mostly off-screen it "fixes" it. Or if the saved location is on a monitor that is no longer there (e.g. my laptop is now by itself without my second monitor) then it correctly moves it to monitor 1.

也就是说,如果我用监视器 2 上的窗口关闭我的 .NET Winform 应用程序,我希望它将窗口大小、位置和状态保存到应用程序设置中,以便稍后在我重新启动应用程序时恢复到监视器 2。如果像上面的 codeproject 示例一样,它包含一些健全性检查,就像保存的位置大部分在屏幕外一样,它会“修复”它,那就太好了。或者,如果保存的位置在不再存在的显示器上(例如,我的笔记本电脑现在单独存在,没有我的第二台显示器),那么它会正确地将其移动到显示器 1。

Any thoughts?

有什么想法吗?

My environment: C#, .NET 3.5 or below, VS2008

我的环境:C#,.NET 3.5 或以下,VS2008

采纳答案by Michael Sorens

The answer provided by VVS was a great help! I found two minor issues with it though, so I am reposting the bulk of his code with these revisions:

VVS 提供的答案很有帮助!不过我发现它有两个小问题,所以我重新发布了他的大部分代码,并进行了这些修订:

(1) The very first time the application runs, the form is opened in a Normal state but is sized such that it appears as just a title bar. I added a conditional in the constructor to fix this.

(1) 应用程序第一次运行时,窗体以正常状态打开,但大小调整为仅显示为标题栏。我在构造函数中添加了一个条件来解决这个问题。

(2) If the application is closed while minimized or maximized the code in OnClosing fails to remember the dimensions of the window in its Normal state. (The 3 lines of code--which I have now commented out--seems reasonable but for some reason just does not work.) Fortunately I had previously solved this problem and have included that code in a new region at the end of the code to track window state as it happens rather than wait for closing.

(2) 如果应用程序在最小化或最大化时关闭,OnClosing 中的代码无法记住正常状态下窗口的尺寸。(这 3 行代码——我现在已经注释掉了——似乎合理,但由于某种原因,它不起作用。)幸运的是,我之前已经解决了这个问题,并将该代码包含在代码末尾的新区域中在发生时跟踪窗口状态,而不是等待关闭。



With these two fixes in place, I have tested:

有了这两个修复程序,我进行了测试:

A. closing in normal state--restores to same size/position and state

A. 在正常状态下平仓——恢复到相同的大小/位置和状态

B. closing in minimized state--restores to normal state with last normal size/position

B. 以最小化状态关闭--恢复到具有最后正常大小/位置的正常状态

C. closing in maximized state--restores to maximized state and remembers its last size/position when one later adjusts to normal state.

C. 以最大化状态关闭——恢复到最大化状态并在以后调整到正常状态时记住其最后的大小/位置。

D. closing on monitor 2--restores to monitor 2.

D. 关闭监视器 2——恢复到监视器 2。

E. closing on monitor 2 then disconnecting monitor 2--restores to same position on monitor 1

E. 关闭监视器 2 然后断开监视器 2--恢复到监视器 1 上的相同位置

David: your code allowed me to achieve points D and E almost effortlessly--not only did you provide a solution for my question, you provided it in a complete program so I had it up and running almost within seconds of pasting it into Visual Studio. So a big thank you for that!

大卫:你的代码让我几乎毫不费力地达到了 D 点和 E 点——你不仅为我的问题提供了解决方案,而且在一个完整的程序中提供了它,所以我几乎在将它粘贴到 Visual Studio 的几秒钟内就启动并运行了它. 非常感谢你!

public partial class MainForm : Form
{
    bool windowInitialized;

    public MainForm()
    {
        InitializeComponent();

        // this is the default
        this.WindowState = FormWindowState.Normal;
        this.StartPosition = FormStartPosition.WindowsDefaultBounds;

        // check if the saved bounds are nonzero and visible on any screen
        if (Settings.Default.WindowPosition != Rectangle.Empty &&
            IsVisibleOnAnyScreen(Settings.Default.WindowPosition))
        {
            // first set the bounds
            this.StartPosition = FormStartPosition.Manual;
            this.DesktopBounds = Settings.Default.WindowPosition;

            // afterwards set the window state to the saved value (which could be Maximized)
            this.WindowState = Settings.Default.WindowState;
        }
        else
        {
            // this resets the upper left corner of the window to windows standards
            this.StartPosition = FormStartPosition.WindowsDefaultLocation;

            // we can still apply the saved size
            // msorens: added gatekeeper, otherwise first time appears as just a title bar!
            if (Settings.Default.WindowPosition != Rectangle.Empty)
            {
                this.Size = Settings.Default.WindowPosition.Size;
            }
        }
        windowInitialized = true;
    }

    private bool IsVisibleOnAnyScreen(Rectangle rect)
    {
        foreach (Screen screen in Screen.AllScreens)
        {
            if (screen.WorkingArea.IntersectsWith(rect))
            {
                return true;
            }
        }

        return false;
    }

    protected override void OnClosed(EventArgs e)
    {
        base.OnClosed(e);

        // only save the WindowState if Normal or Maximized
        switch (this.WindowState)
        {
            case FormWindowState.Normal:
            case FormWindowState.Maximized:
                Settings.Default.WindowState = this.WindowState;
                break;

            default:
                Settings.Default.WindowState = FormWindowState.Normal;
                break;
        }

        # region msorens: this code does *not* handle minimized/maximized window.

        // reset window state to normal to get the correct bounds
        // also make the form invisible to prevent distracting the user
        //this.Visible = false;
        //this.WindowState = FormWindowState.Normal;
        //Settings.Default.WindowPosition = this.DesktopBounds;

        # endregion

        Settings.Default.Save();
    }

    # region window size/position
    // msorens: Added region to handle closing when window is minimized or maximized.

    protected override void OnResize(EventArgs e)
    {
        base.OnResize(e);
        TrackWindowState();
    }

    protected override void OnMove(EventArgs e)
    {
        base.OnMove(e);
        TrackWindowState();
    }

    // On a move or resize in Normal state, record the new values as they occur.
    // This solves the problem of closing the app when minimized or maximized.
    private void TrackWindowState()
    {
        // Don't record the window setup, otherwise we lose the persistent values!
        if (!windowInitialized) { return; }

        if (WindowState == FormWindowState.Normal)
        {
            Settings.Default.WindowPosition = this.DesktopBounds;
        }
    }

    # endregion window size/position
}

回答by Cheeso

If you have multiple monitors, I believe the screen UI dimensions are simply larger. So the normal "1 monitor" approach of storing and restoring the location will just work. I haven't tried this because I am away from my second monitor but it shouldn't be hard to test. The way you asked the Question is seems like you haven't tested it.

如果您有多个显示器,我相信屏幕 UI 尺寸会更大。因此,存储和恢复位置的正常“1 个监视器”方法将起作用。我没有尝试过这个,因为我远离我的第二台显示器,但测试应该不难。你问这个问题的方式似乎你还没有测试过。

Your second requirement will mean you will have to check the max sceen dimensions when restoring the app, and then reposition as necessary. To do this latter bit, I use this code:

您的第二个要求意味着您必须在恢复应用程序时检查最大屏幕尺寸,然后根据需要重新定位。为了做到这一点,我使用以下代码:

    private System.Drawing.Rectangle ConstrainToScreen(System.Drawing.Rectangle bounds)
    {
        Screen screen = Screen.FromRectangle(bounds);
        System.Drawing.Rectangle workingArea = screen.WorkingArea;
        int width = Math.Min(bounds.Width, workingArea.Width);
        int height = Math.Min(bounds.Height, workingArea.Height);
        // mmm....minimax            
        int left = Math.Min(workingArea.Right - width, Math.Max(bounds.Left, workingArea.Left));
        int top = Math.Min(workingArea.Bottom - height, Math.Max(bounds.Top, workingArea.Top));
        return new System.Drawing.Rectangle(left, top, width, height);
    }

I call this method when restoring the form. I store the screen geometry in the registry on form close, and then read the geometry on form open. I get the bounds, but then constrain the restored bounds to the actual current screen, using the method above.

我在恢复表单时调用此方法。我在表单关闭时将屏幕几何图形存储在注册表中,然后在表单打开时读取几何图形。我得到了边界,然后使用上面的方法将恢复的边界限制到实际的当前屏幕。

Save on close:

关闭时保存:

      // store the size of the form
      int w = 0, h = 0, left = 0, top = 0;
      if (this.Bounds.Width < this.MinimumSize.Width || this.Bounds.Height < this.MinimumSize.Height)
      {
          // The form is currently minimized.  
          // RestoreBounds is the size of the window prior to last minimize action.
          w = this.RestoreBounds.Width;
          h = this.RestoreBounds.Height;
          left = this.RestoreBounds.Location.X;
          top = this.RestoreBounds.Location.Y;
      }
      else
      {
          w = this.Bounds.Width;
          h = this.Bounds.Height;
          left = this.Location.X;
          top = this.Location.Y;
      }
      AppCuKey.SetValue(_rvn_Geometry,
        String.Format("{0},{1},{2},{3},{4}",
              left, top, w, h, (int)this.WindowState));

Restore on form open:

在表单打开时恢复:

    // restore the geometry of the form
    string s = (string)AppCuKey.GetValue(_rvn_Geometry);
    if (!String.IsNullOrEmpty(s))
    {
        int[] p = Array.ConvertAll<string, int>(s.Split(','),
                         new Converter<string, int>((t) => { return Int32.Parse(t); }));
        if (p != null && p.Length == 5)
            this.Bounds = ConstrainToScreen(new System.Drawing.Rectangle(p[0], p[1], p[2], p[3]));
    }

回答by VVS

Try this code. Points of interest:

试试这个代码。兴趣点:

  • Checks if the window is (partially) visible on any screen's working area. E.g. dragging it behind the task bar or moving it completely offscreen resets the position to windows default.
  • Saves the correct bounds even if the Form is minimized or maximized (common error)
  • Saves the WindowState correctly. Saving FormWindowState.Minimized is disabled by design.
  • 检查窗口在任何屏幕的工作区域是否(部分)可见。例如,将其拖到任务栏后面或将其完全移出屏幕会将位置重置为 Windows 默认值。
  • 即使最小化或最大化 Form 也保存正确的边界(常见错误)
  • 正确保存 WindowState。保存 FormWindowState.Minimized 被设计禁用。

The bounds and state are stored in the appsettings with their corresponding type so there's no need to do any string parsing. Let the framework do its serialization magic.

边界和状态以其对应的类型存储在 appsettings 中,因此无需进行任何字符串解析。让框架发挥它的序列化魔法。

public partial class MainForm : Form
{
    public MainForm()
    {
        InitializeComponent();

        // this is the default
        this.WindowState = FormWindowState.Normal;
        this.StartPosition = FormStartPosition.WindowsDefaultBounds;

        // check if the saved bounds are nonzero and visible on any screen
        if (Settings.Default.WindowPosition != Rectangle.Empty &&
            IsVisibleOnAnyScreen(Settings.Default.WindowPosition))
        {
            // first set the bounds
            this.StartPosition = FormStartPosition.Manual;
            this.DesktopBounds = Settings.Default.WindowPosition;

            // afterwards set the window state to the saved value (which could be Maximized)
            this.WindowState = Settings.Default.WindowState;
        }
        else
        {
            // this resets the upper left corner of the window to windows standards
            this.StartPosition = FormStartPosition.WindowsDefaultLocation;

            // we can still apply the saved size
            this.Size = Settings.Default.WindowPosition.Size;
        }
    }

    private bool IsVisibleOnAnyScreen(Rectangle rect)
    {
        foreach (Screen screen in Screen.AllScreens)
        {
            if (screen.WorkingArea.IntersectsWith(rect))
            {
                return true;
            }
        }

        return false;
    }

    protected override void OnClosed(EventArgs e)
    {
        base.OnClosed(e);

        // only save the WindowState if Normal or Maximized
        switch (this.WindowState)
        {
            case FormWindowState.Normal:
            case FormWindowState.Maximized:
                Settings.Default.WindowState = this.WindowState;
                break;

            default:
                Settings.Default.WindowState = FormWindowState.Normal;
                break;
        }

        // reset window state to normal to get the correct bounds
        // also make the form invisible to prevent distracting the user
        this.Visible = false;
        this.WindowState = FormWindowState.Normal;

        Settings.Default.WindowPosition = this.DesktopBounds;
        Settings.Default.Save();
    }
}

The settings file for reference:

参考设置文件:

<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" GeneratedClassNamespace="ScreenTest" GeneratedClassName="Settings">
    <Profiles />
    <Settings>
        <Setting Name="WindowPosition" Type="System.Drawing.Rectangle" Scope="User">
            <Value Profile="(Default)">0, 0, 0, 0</Value>
        </Setting>
        <Setting Name="WindowState" Type="System.Windows.Forms.FormWindowState" Scope="User">
            <Value Profile="(Default)">Normal</Value>
        </Setting>
    </Settings>
</SettingsFile>

回答by CallMeLaNN

This is the perfect one I think based on your answers and comments.

根据您的回答和评论,这是我认为的完美选择。



This solution is to save/restore form size and positionwith multi monitors+ multi document, multi formor multi main formsupport. It is not MDIform but Microsoft Word like multi document with different main form instance.

这个解决方案是保存/恢复形式的大小和位置多显示器+多文档多形态多层主要形式的支持。它不是 MDI表单,而是 Microsoft Word,类似于具有不同主表单实例的多文档。

Thanks to VVS, msorens and Ian Goldby. I merge the solution from VVS, msorens and MSDN Application.Run Method (ApplicationContext)example to make the multi MainForm but not MDI.

感谢 VVS、msorens 和 Ian Goldby。我合并了来自 VVS、msorens 和 MSDN Application.Run Method (ApplicationContext)示例的解决方案,以制作多 MainForm 而不是 MDI。

This fix of includes the comment from Ian Goldby which uses Form.RestoreBoundsto eliminate OnResize(), OnMove()and TrackWindowState().

此修复程序包括来自 Ian Goldby 的评论,该评论Form.RestoreBounds用于消除OnResize(),OnMove()TrackWindowState()

I also fix to remember the Monitor when the Form move to the other Monitor and getting maximized before exit because I not tracking the OnResize, OnMove. By this fix, this solution support Windows 7 Snap featurewhich you can drag the titlebar or Win+Arrow key to snap Form window into any monitor edge or make it maximized/normal as well as minimized.

我还修复了当窗体移动到另一个监视器并在退出前最大化时记住监视器,因为我没有跟踪 OnResize、OnMove。通过此修复,此解决方案支持Windows 7 Snap 功能,您可以拖动标题栏或 Win+箭头键将窗体窗口对齐到任何监视器边缘或使其最大化/正常以及最小化。

This solution implemented in Program but not in the main Form to support multi main Form. However you can use for single main Form also.

该解决方案在程序中实现,但不在主窗体中实现,以支持多主窗体。但是,您也可以用于单个主表单。

using System;
using System.Collections.Generic;
using System.Windows.Forms;
using SimpleTestForm.Properties;
using System.Drawing;

namespace SimpleTestForm
{
    static class Program
    {
        static MultiMainFormAppContext appContext;

        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);

            appContext = new MultiMainFormAppContext();
            Application.Run(appContext);
        }

        /// <summary>
        /// Create a new MainForm and restore the form size and position if necessary. This method can be called like from Menu File > New click event.
        /// </summary>
        /// <returns></returns>
        public static MainForm createNewMainForm()
        {
            return appContext.createNewMainForm();
        }

        /// <summary>
        /// Get the current active MainForm event if a dialog is opened. Useful to create Dictionary (MainForm, T) to store Form/document dependent field. Please set the Owner of child form to prevent null reference exception.
        /// </summary>
        /// <returns></returns>
        public static MainForm GetCurrentMainFormInstance()
        {
            Form mainForm = Form.ActiveForm;
            while (!(mainForm is MainForm) && mainForm.Owner != null)
                mainForm = mainForm.Owner;
            return mainForm as MainForm;
        }
    }

    class MultiMainFormAppContext : ApplicationContext
    {
        List<MainForm> mainForms = new List<MainForm>();
        Point newRestoredLocation = Point.Empty;

        internal MultiMainFormAppContext()
        {
            createNewMainForm();
        }

        internal MainForm createNewMainForm()
        {
            MainForm mainForm = new MainForm();
            mainForm.FormClosed += new FormClosedEventHandler(mainForm_FormClosed);
            mainForm.LocationChanged += new EventHandler(mainForm_LocationChanged);
            RestoreFormSizeNPosition(mainForm);
            PreventSameLocation(mainForm);
            mainForms.Add(mainForm);
            mainForm.Show();
            return mainForm;
        }

        private void PreventSameLocation(MainForm mainForm)
        {
            const int distance = 20;
            foreach (MainForm otherMainForm in mainForms)
            {
                if (Math.Abs(otherMainForm.Location.X - mainForm.Location.X) < distance &&
                    Math.Abs(otherMainForm.Location.Y - mainForm.Location.Y) < distance)
                    mainForm.Location = new Point(mainForm.Location.X + distance, mainForm.Location.Y + distance);
            }
        }

        /// <summary>
        /// Restore the form size and position with multi monitor support.
        /// </summary>
        private void RestoreFormSizeNPosition(MainForm mainForm)
        {
            // this is the default
            mainForm.WindowState = FormWindowState.Normal;
            mainForm.StartPosition = FormStartPosition.WindowsDefaultBounds;

            // check if the saved bounds are nonzero and visible on any screen
            if (Settings.Default.WindowPosition != Rectangle.Empty &&
                IsVisibleOnAnyScreen(Settings.Default.WindowPosition))
            {
                // first set the bounds
                mainForm.StartPosition = FormStartPosition.Manual;
                mainForm.DesktopBounds = Settings.Default.WindowPosition;

                // afterwards set the window state to the saved value (which could be Maximized)
                mainForm.WindowState = Settings.Default.WindowState;
            }
            else
            {
                // this resets the upper left corner of the window to windows standards
                mainForm.StartPosition = FormStartPosition.WindowsDefaultLocation;

                // we can still apply the saved size if not empty
                if (Settings.Default.WindowPosition != Rectangle.Empty)
                {
                    mainForm.Size = Settings.Default.WindowPosition.Size;
                }
            }
        }

        private void SaveFormSizeNPosition(MainForm mainForm)
        {
            // only save the WindowState as Normal or Maximized
            Settings.Default.WindowState = FormWindowState.Normal;
            if (mainForm.WindowState == FormWindowState.Normal || mainForm.WindowState == FormWindowState.Maximized)
                Settings.Default.WindowState = mainForm.WindowState;

            if (mainForm.WindowState == FormWindowState.Normal)
            {
                Settings.Default.WindowPosition = mainForm.DesktopBounds;
            }
            else
            {
                if (newRestoredLocation == Point.Empty)
                    Settings.Default.WindowPosition = mainForm.RestoreBounds;
                else
                    Settings.Default.WindowPosition = new Rectangle(newRestoredLocation, mainForm.RestoreBounds.Size);
            }

            Settings.Default.Save();
        }

        private bool IsVisibleOnAnyScreen(Rectangle rect)
        {
            foreach (Screen screen in Screen.AllScreens)
            {
                if (screen.WorkingArea.IntersectsWith(rect))
                    return true;
            }
            return false;
        }

        void mainForm_LocationChanged(object sender, EventArgs e)
        {
            MainForm mainForm = sender as MainForm;
            if (mainForm.WindowState == FormWindowState.Maximized)
            {
                // get the center location of the form incase like RibbonForm will be bigger and maximized Location wll be negative value that Screen.FromPoint(mainForm.Location) will going to the other monitor resides on the left or top of primary monitor.
                // Another thing, you might consider the form is in the monitor even if the location (top left corner) is on another monitor because majority area is on the monitor, so center point is the best way.
                Point centerFormMaximized = new Point (mainForm.DesktopBounds.Left + mainForm.DesktopBounds.Width/2, mainForm.DesktopBounds.Top + mainForm.DesktopBounds.Height/2);
                Point centerFormRestored = new Point(mainForm.RestoreBounds.Left + mainForm.RestoreBounds.Width / 2, mainForm.RestoreBounds.Top + mainForm.RestoreBounds.Height / 2);
                Screen screenMaximized = Screen.FromPoint(centerFormMaximized);
                Screen screenRestored = Screen.FromPoint(centerFormRestored);
                // we need to change the Location of mainForm.RestoreBounds to the new screen where the form currently maximized.
                // RestoreBounds does not update the Location if you change the screen but never restore to FormWindowState.Normal
                if (screenMaximized.DeviceName != screenRestored.DeviceName)
                {
                    newRestoredLocation = mainForm.RestoreBounds.Location;
                    int screenOffsetX = screenMaximized.Bounds.Location.X - screenRestored.Bounds.Location.X;
                    int screenOffsetY = screenMaximized.Bounds.Location.Y - screenRestored.Bounds.Location.Y;
                    newRestoredLocation.Offset(screenOffsetX, screenOffsetY);
                    return;
                }
            }
            newRestoredLocation = Point.Empty;
        }

        void mainForm_FormClosed(object sender, FormClosedEventArgs e)
        {
            MainForm mainForm = sender as MainForm;
            SaveFormSizeNPosition(mainForm);
            mainForm.FormClosed -= new FormClosedEventHandler(mainForm_FormClosed);
            mainForm.LocationChanged -= new EventHandler(mainForm_LocationChanged);
            mainForm.Dispose();
            mainForms.Remove(mainForm);
            if (mainForms.Count == 0) ExitThread();
        }
    }
}

Edit: PreventSameLocation method added to make sure the 2nd form opened not exactly on top of the 1st form and user will notice the newly opened form.

编辑:添加了 preventSameLocation 方法以确保打开的第二个表单不完全位于第一个表单的顶部,并且用户会注意到新打开的表单。

回答by ShadowChaser

Most of the other solutions here rely on manually figuring out the current positioning of each monitor. The edge cases are extremely difficult to figure out, and very few apps can get it right rolling their own.

这里的大多数其他解决方案都依赖于手动确定每个显示器的当前位置。边缘情况极难弄清楚,很少有应用程序可以自己正确运行。

The SetWindowPlacement function within Windows itself correctly handles all of the edge cases - if the window would be positioned off of a visible screen, it adjusts it accordingly.

Windows 本身内的 SetWindowPlacement 函数可以正确处理所有边缘情况 - 如果窗口位于可见屏幕之外,它会相应地调整它。

The best example I've seen in C# is on David Rickard's blog. Not only does it show how to use SetWindowPlacement, it also shows how to serialize the entire result. http://blogs.msdn.com/b/davidrickard/archive/2010/03/09/saving-window-size-and-location-in-wpf-and-winforms.aspx

我在 C# 中看到的最好的例子是 David Rickard 的博客。它不仅展示了如何使用 SetWindowPlacement,还展示了如何序列化整个结果。 http://blogs.msdn.com/b/davidrickard/archive/2010/03/09/ Saving-window-size-and-location-in-wpf-and-winforms.aspx

回答by Judah Sali

This is an old question, but here is a VB version based on the previous answers.

这是一个老问题,但这里是基于先前答案的 VB 版本。

One problem with the answers suggested by VVS and Michael Sorens is that a saved position that only shows a couple of pixels on a screen counts as visible. This solution requires at least 50x50 pixels in intersection before restoring the previous location.

VVS 和 Michael Sorens 建议的答案的一个问题是,仅在屏幕上显示几个像素的已保存位置算作可见。在恢复之前的位置之前,此解决方案需要至少 50x50 像素的交叉点。

Settings:

设置:

<Settings>
  <Setting Name="WindowState" Type="System.Windows.Forms.FormWindowState" Scope="User">
    <Value Profile="(Default)">Normal</Value>
  </Setting>
  <Setting Name="WindowBounds" Type="System.Drawing.Rectangle" Scope="User">
    <Value Profile="(Default)">10, 10, 800, 600</Value>
  </Setting>
</Settings>

Form:

形式:

Partial Public Class MainForm

    Private loadingComplete As Boolean = False

    Public Sub New()

        InitializeComponent()
        RestoreWindowLocation()

    End Sub

    Private Sub MainForm_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load

        loadingComplete = True

    End Sub

    Private Sub MainForm_Resize(sender As System.Object, e As System.EventArgs) Handles MyBase.Resize

         TrackWindowLocation()

     End Sub

     Private Sub MainForm_Move(sender As System.Object, e As System.EventArgs) Handles MyBase.Move

         TrackWindowLocation()

     End Sub

    Private Sub MainForm_FormClosing(sender As System.Object, e As System.Windows.Forms.FormClosingEventArgs) Handles MyBase.FormClosing

        SaveWindowLocation()

     End Sub


    Private Sub RestoreWindowLocation()

        If IsRectangleVisible(My.Settings.WindowBounds) Then
            Me.StartPosition = FormStartPosition.Manual
            Me.DesktopBounds = My.Settings.WindowBounds
        End If

        If Not My.Settings.WindowState = FormWindowState.Minimized Then
            Me.WindowState = My.Settings.WindowState
        End If

    End Sub

    Private Sub TrackWindowLocation()

        If loadingComplete Then
            If Me.WindowState = FormWindowState.Normal Then
                My.Settings.WindowBounds = Me.DesktopBounds
                My.Settings.WindowState = Me.WindowState
            End If
        End If

    End Sub

    Private Sub SaveWindowLocation()

        If Not Me.WindowState = FormWindowState.Minimized Then
            My.Settings.WindowState = Me.WindowState
        End If

        If Me.WindowState = FormWindowState.Normal Then
            My.Settings.WindowBounds = Me.DesktopBounds
        End If

        My.Settings.Save()

    End Sub

    Private Function IsRectangleVisible(Rectangle As Rectangle) As Boolean

        For Each screen As Screen In screen.AllScreens
            Dim r As Rectangle = Rectangle.Intersect(Rectangle, screen.WorkingArea)
            If Not r.IsEmpty Then
                If r.Width > 50 And r.Height > 50 Then Return True
            End If
        Next

        Return False

    End Function

End Class