使用多个监视器恢复窗口大小/位置

Mic*_*ens 33 c# multiple-monitors appsettings winforms

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

例子:

但我还没有找到使用多个监视器执行此操作的代码.

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

有什么想法吗?

我的环境:C#,.NET 3.5或更低版本,VS2008

VVS*_*VVS 37

试试这个代码.兴趣点:

  • 检查窗口在任何屏幕的工作区域是否(部分)可见.例如,将其拖动到任务栏后面或将其完全移出屏幕会将位置重置为Windows默认值.
  • 即使窗体最小化或最大化(常见错误),也可以保存正确的边界
  • 正确保存WindowState.保存FormWindowState.Minimized已被设计禁用.

边界和状态以其相应的类型存储在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();
    }
}
Run Code Online (Sandbox Code Playgroud)

设置文件供参考:

<?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>
Run Code Online (Sandbox Code Playgroud)


Mic*_*ens 27

VVS提供的答案是一个很大的帮助!我发现它有两个小问题,所以我在这些修订版中重新发布了大部分代码:

(1)应用程序第一次运行时,表单以"正常"状态打开,但其大小应使其显示为标题栏.我在构造函数中添加了一个条件来修复它.

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


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

A.在正常状态下关闭 - 恢复到相同的大小/位置和状态

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

C.在最大化状态下关闭 - 恢复到最大化状态并在后者调整到正常状态时记住它的最后大小/位置.

D.关闭监视器2 - 恢复监视器2.

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

大卫:你的代码让我几乎毫不费力地获得了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
}
Run Code Online (Sandbox Code Playgroud)

  • 你应该使用[Form.RestoreBounds](http://msdn.microsoft.com/en-us/library/system.windows.forms.form.restorebounds.aspx)来获取[WindowState]时窗体的位置和大小](http://msdn.microsoft.com/en-us/library/system.windows.forms.form.windowstate.aspx)不是[普通](http://msdn.microsoft.com/en-us/库/ system.windows.forms.formwindowstate.aspx).那你就不需要TrackWindowState()了.另见Cheeso的答案如下. (4认同)

Sha*_*ser 9

这里的大多数其他解决方案都依赖于手动确定每个监视器的当前位置.边缘情况非常难以弄清楚,并且很少有应用程序能够正确地完成自己的工作.

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

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