C# 如何在Windows窗体应用程序设置中记录窗口位置

C# 如何在Windows窗体应用程序设置中记录窗口位置,c#,.net,winforms,settings,C#,.net,Winforms,Settings,这似乎是一个标准要求:下次用户启动应用程序时,以与以前相同的位置和状态打开窗口。这是我的愿望清单: 窗户位置和原来一样 除非屏幕已调整大小且旧位置现在已脱离屏幕 分裂分子应该保持他们的地位 选项卡容器应保留其选择 某些下拉列表应保留其选择 窗口状态(最大化、最小化、正常)与以前相同。 也许你永远不应该开始,我还没决定 我将添加我当前的解决方案以及限制。我发现的最简单的解决方案是将数据绑定与应用程序设置结合使用。我绑定窗口上的location和clientSize属性以及拆分器上的sp

这似乎是一个标准要求:下次用户启动应用程序时,以与以前相同的位置和状态打开窗口。这是我的愿望清单:

  • 窗户位置和原来一样
    • 除非屏幕已调整大小且旧位置现在已脱离屏幕
  • 分裂分子应该保持他们的地位
  • 选项卡容器应保留其选择
  • 某些下拉列表应保留其选择
  • 窗口状态(最大化、最小化、正常)与以前相同。
    • 也许你永远不应该开始,我还没决定

我将添加我当前的解决方案以及限制。

我发现的最简单的解决方案是将数据绑定与应用程序设置结合使用。我绑定窗口上的location和clientSize属性以及拆分器上的splitterDistance

缺点:

  • 如果在最小化时关闭窗口,则下次将隐藏打开窗口。把窗户找回来真的很难
  • 如果在最大化时关闭窗口,则会打开整个屏幕,但不会最大化(小问题)
  • 使用右上角或左下角调整窗口的大小非常难看。我猜这两个数据绑定属性正在互相争斗

如果你想尝试这种奇怪的行为,我发布了一篇使用这种技术的文章。

一个可以使用设置来存储信息的黑客。您所要做的就是将所需的属性(例如form.Size和form.Location)绑定到一个特定的设置,它会自动保存和更新

我的另一个选择是围绕应用程序设置编写更多自定义代码,并在formLoad和formClosed上执行。这不使用数据绑定

缺点:

  • 更多代码需要编写
  • 非常精细。在formLoad上设置属性的顺序令人困惑。例如,在设置拆分器距离之前,必须确保已设置窗口大小
现在,这是我首选的解决方案,但似乎工作量太大了。为了减少工作量,我创建了一个WindowsSettings类,将窗口位置、大小、状态和任何拆分器位置序列化为单个应用程序设置。然后,我可以为应用程序中的每个表单创建该类型的设置,关闭时保存,加载时恢复

我发布了,包括WindowsSettings类和一些使用它的表单。有关将其添加到项目的说明包含在WindowSettings.cs文件中。最棘手的部分是找出如何添加自定义类型的应用程序设置。你选择浏览。。。从类型下拉列表中,然后手动输入名称空间和类名。项目中的类型不会显示在列表中

更新:我添加了一些静态方法来简化添加到每个表单的样板代码。按照说明将WindowsSettings类添加到项目中并创建应用程序设置后,下面是一个代码示例,必须添加到要记录和恢复其位置的每个表单中

    private void MyForm_FormClosing(object sender, FormClosingEventArgs e)
    {
        Settings.Default.CustomWindowSettings = WindowSettings.Record(
            Settings.Default.CustomWindowSettings,
            this, 
            splitContainer1);
    }

    private void MyForm_Load(object sender, EventArgs e)
    {
        WindowSettings.Restore(
            Settings.Default.CustomWindowSettings, 
            this, 
            splitContainer1);
    }

您可以使用应用程序设置设置哪些控件属性将被持久化,在Form_closed事件中,您必须使用应用程序设置上的save方法将这些属性写入磁盘:

Properties.Settings.Default.Save();

下面是我自己使用的几个例子。它只考虑主监视器,因此如果在多个监视器上使用,则最好以不同的方式处理它

Size size;
int x;
int y;
if (WindowState.Equals(FormWindowState.Normal))
{
    size = Size;
    if (Location.X + size.Width > Screen.PrimaryScreen.Bounds.Width)
        x = Screen.PrimaryScreen.Bounds.Width - size.Width;
    else
        x = Location.X;
    if (Location.Y + Size.Height > Screen.PrimaryScreen.Bounds.Height)
        y = Screen.PrimaryScreen.Bounds.Height - size.Height;
    else
        y = Location.Y;
}
else
{
size = RestoreBounds.Size;
x = (Screen.PrimaryScreen.Bounds.Width - size.Width)/2;
y = (Screen.PrimaryScreen.Bounds.Height - size.Height)/2;
}
Properties.Settings.Position.AsPoint = new Point(x, y); // Property setting is type of Point
Properties.Settings.Size.AsSize = size;                 // Property setting is type of Size
Properties.Settings.SplitterDistance.Value = splitContainer1.SplitterDistance; // Property setting is type of int
Properties.Settings.IsMaximized = WindowState == FormWindowState.Maximized;    // Property setting is type of bool
Properties.Settings.DropDownSelection = DropDown1.SelectedValue;
Properties.Settings.Save();

我为要保存的每个值进行设置,并使用如下代码:

private void MainForm_Load(object sender, EventArgs e) {
  RestoreState();
}

private void MainForm_FormClosing(object sender, FormClosingEventArgs e) {
  SaveState();
}

private void SaveState() {
  if (WindowState == FormWindowState.Normal) {
    Properties.Settings.Default.MainFormLocation = Location;
    Properties.Settings.Default.MainFormSize = Size;
  } else {
    Properties.Settings.Default.MainFormLocation = RestoreBounds.Location;
    Properties.Settings.Default.MainFormSize = RestoreBounds.Size;
  }
  Properties.Settings.Default.MainFormState = WindowState;
  Properties.Settings.Default.SplitterDistance = splitContainer1.SplitterDistance;
  Properties.Settings.Default.Save();
}

private void RestoreState() {
  if (Properties.Settings.Default.MainFormSize == new Size(0, 0)) {
    return; // state has never been saved
  }
  StartPosition = FormStartPosition.Manual;
  Location = Properties.Settings.Default.MainFormLocation;
  Size = Properties.Settings.Default.MainFormSize;
  // I don't like an app to be restored minimized, even if I closed it that way
  WindowState = Properties.Settings.Default.MainFormState == 
    FormWindowState.Minimized ? FormWindowState.Normal : Properties.Settings.Default.MainFormState;
  splitContainer1.SplitterDistance = Properties.Settings.Default.SplitterDistance;
}
using System;
using System.Configuration;
using System.Reflection;
using System.Windows.Forms;

namespace CustomForm
{
  public class MyCustomForm : Form
  {
    private ApplicationSettingsBase _appSettings = null;
    private string _settingName = "";

    public Form() : base() { }

    public Form(ApplicationSettingsBase settings, string settingName)
      : base()
    {
      _appSettings = settings;
      _settingName = settingName;

      this.Load += new EventHandler(Form_Load);
      this.FormClosing += new FormClosingEventHandler(Form_FormClosing);
    }

    private void Form_Load(object sender, EventArgs e)
    {
      if (_appSettings == null) return;

      PropertyInfo settingProperty = _appSettings.GetType().GetProperty(_settingName);
      if (settingProperty == null) return;

      WindowSettings previousSettings = settingProperty.GetValue(_appSettings, null) as WindowSettings;
      if (previousSettings == null) return;

      previousSettings.Restore(this);
    }

    private void Form_FormClosing(object sender, FormClosingEventArgs e)
    {
      if (_appSettings == null) return;

      PropertyInfo settingProperty = _appSettings.GetType().GetProperty(_settingName);
      if (settingProperty == null) return;

      WindowSettings previousSettings = settingProperty.GetValue(_appSettings, null) as WindowSettings;
      if (previousSettings == null)
        previousSettings = new WindowSettings();

      previousSettings.Record(this);

      settingProperty.SetValue(_appSettings, previousSettings, null);

      _appSettings.Save();
    }
  }
}

请记住,重新编译会清除存储设置的配置文件,因此请在保存和还原之间进行测试,而无需更改代码。

下面的示例演示了如何执行此操作

  • 在关闭表单并保存表单大小时,会调用SavePreferences,并显示一个标志,指示表单是否最大化(在这个版本中,如果最小化,我不会保存-下次将恢复或最大化)

  • LoadPreferences是从OnLoad调用的

  • 首先保存设计时窗口状态并将其设置为正常。只有当窗体的Windows状态为正常时,才能成功设置窗体大小

  • 接下来,从持久化设置还原大小

  • 现在,确保表单适合您的屏幕(调用FitToScreen)。自上次运行应用程序以来,屏幕分辨率可能已更改

  • 最后,将WindowsState设置回最大化(如果保持最大化),或设置回先前保存的设计时值

很明显,这可以用来保持起始位置,以及是否在关闭时最小化表单-我不需要这样做。表单上控件的其他设置(如拆分器位置和选项卡容器)非常简单

private void FitToScreen()
{
    if (this.Width > Screen.PrimaryScreen.WorkingArea.Width)
    {
        this.Width = Screen.PrimaryScreen.WorkingArea.Width;
    }
    if (this.Height > Screen.PrimaryScreen.WorkingArea.Height)
    {
        this.Height = Screen.PrimaryScreen.WorkingArea.Height;
    }
}   
private void LoadPreferences()
{
    // Called from Form.OnLoad

    // Remember the initial window state and set it to Normal before sizing the form
    FormWindowState initialWindowState = this.WindowState;
    this.WindowState = FormWindowState.Normal;
    this.Size = UserPreferencesManager.LoadSetting("_Size", this.Size);
    _currentFormSize = Size;
    // Fit to the current screen size in case the screen resolution
    // has changed since the size was last persisted.
    FitToScreen();
    bool isMaximized = UserPreferencesManager.LoadSetting("_Max", initialWindowState == FormWindowState.Maximized);
    WindowState = isMaximized ? FormWindowState.Maximized : FormWindowState.Normal;
}
private void SavePreferences()
{
    // Called from Form.OnClosed
    UserPreferencesManager.SaveSetting("_Size", _currentFormSize);
    UserPreferencesManager.SaveSetting("_Max", this.WindowState == FormWindowState.Maximized);
    ... save other settings
}

x

根据Don Kirkby接受的答案和他编写的WindowsSettings类,您可以从标准表单派生CustomForm,以减少为每个表单编写的相同代码量,可能如下所示:

private void MainForm_Load(object sender, EventArgs e) {
  RestoreState();
}

private void MainForm_FormClosing(object sender, FormClosingEventArgs e) {
  SaveState();
}

private void SaveState() {
  if (WindowState == FormWindowState.Normal) {
    Properties.Settings.Default.MainFormLocation = Location;
    Properties.Settings.Default.MainFormSize = Size;
  } else {
    Properties.Settings.Default.MainFormLocation = RestoreBounds.Location;
    Properties.Settings.Default.MainFormSize = RestoreBounds.Size;
  }
  Properties.Settings.Default.MainFormState = WindowState;
  Properties.Settings.Default.SplitterDistance = splitContainer1.SplitterDistance;
  Properties.Settings.Default.Save();
}

private void RestoreState() {
  if (Properties.Settings.Default.MainFormSize == new Size(0, 0)) {
    return; // state has never been saved
  }
  StartPosition = FormStartPosition.Manual;
  Location = Properties.Settings.Default.MainFormLocation;
  Size = Properties.Settings.Default.MainFormSize;
  // I don't like an app to be restored minimized, even if I closed it that way
  WindowState = Properties.Settings.Default.MainFormState == 
    FormWindowState.Minimized ? FormWindowState.Normal : Properties.Settings.Default.MainFormState;
  splitContainer1.SplitterDistance = Properties.Settings.Default.SplitterDistance;
}
using System;
using System.Configuration;
using System.Reflection;
using System.Windows.Forms;

namespace CustomForm
{
  public class MyCustomForm : Form
  {
    private ApplicationSettingsBase _appSettings = null;
    private string _settingName = "";

    public Form() : base() { }

    public Form(ApplicationSettingsBase settings, string settingName)
      : base()
    {
      _appSettings = settings;
      _settingName = settingName;

      this.Load += new EventHandler(Form_Load);
      this.FormClosing += new FormClosingEventHandler(Form_FormClosing);
    }

    private void Form_Load(object sender, EventArgs e)
    {
      if (_appSettings == null) return;

      PropertyInfo settingProperty = _appSettings.GetType().GetProperty(_settingName);
      if (settingProperty == null) return;

      WindowSettings previousSettings = settingProperty.GetValue(_appSettings, null) as WindowSettings;
      if (previousSettings == null) return;

      previousSettings.Restore(this);
    }

    private void Form_FormClosing(object sender, FormClosingEventArgs e)
    {
      if (_appSettings == null) return;

      PropertyInfo settingProperty = _appSettings.GetType().GetProperty(_settingName);
      if (settingProperty == null) return;

      WindowSettings previousSettings = settingProperty.GetValue(_appSettings, null) as WindowSettings;
      if (previousSettings == null)
        previousSettings = new WindowSettings();

      previousSettings.Record(this);

      settingProperty.SetValue(_appSettings, previousSettings, null);

      _appSettings.Save();
    }
  }
}
要使用此选项,请在构造函数中传递应用程序设置类和设置名称:

CustomForm.MyCustomForm f = new CustomForm.MyCustomForm(Properties.Settings.Default, "formSettings");
这使用反射从设置类获取/设置以前的设置。将Save调用放入Form_关闭例程可能不是最佳选择,可以在主应用程序退出时删除该调用并保存设置文件


要将其用作常规表单,只需使用无参数构造函数。

有一个WindowsState属性可供参考,以检查最小化或最大化的窗口。但是,当窗口处于这两种状态之一时,我无法评论它的大小。是的,我尝试了WindowsState属性。它的行为很奇怪。可能与调整大小的问题类似,即同时更改多个数据绑定属性会导致争用和fl