如何在Windows窗体应用程序设置中记录窗口位置
这似乎是一个标准要求:下一次用户启动应用程序时,请以与以前相同的位置和状态打开窗口。这是我的愿望清单:
- 分离器应保持其位置
- 标签容器应保留其选择
- 一些下拉菜单应保留其选择
我将添加当前的解决方案作为答案以及限制。
解决方案
我找到的最简单的解决方案是在应用程序设置中使用数据绑定。我将窗口上的location和clientSize属性与拆分器上的splitterDistance绑定在一起。
缺点:
- 如果在最小化的情况下关闭窗口,则下次将其隐藏。真的很难把窗户拉回来。
- 如果在最大化的情况下关闭窗口,则它将打开以填充整个屏幕,但不会最大化(次要问题)。
- 使用右上角或者左下角调整窗口大小非常难看。我猜这两个数据绑定属性正在相互斗争。
如果我们想尝试这种奇怪的行为,我将发布使用此技术的示例解决方案。
我们可以使用"设置"来存储该信息。我们要做的就是将所需的属性(例如form.Size和form.Location)绑定到特定设置,然后将其自动保存和更新。
我的另一个选择是围绕应用程序设置编写更多自定义代码,并在formLoad和formClosed上执行它。这不使用数据绑定。
缺点:
- 需要编写更多代码。
- 很乖我们在formLoad上设置属性的顺序令人困惑。例如,在设置分隔线距离之前,必须确保已设置窗口大小。
现在,这是我的首选解决方案,但似乎工作量太大。为了减少工作量,我创建了一个WindowSettings类,该类将窗口的位置,大小,状态和所有拆分器位置序列化为单个应用程序设置。然后,我可以为我的应用程序中的每个表单创建该类型的设置,保存在关闭状态,并在加载时恢复。
我发布了源代码,包括WindowSettings类和一些使用它的表单。 WindowSettings.cs文件中包含有关将其添加到项目中的说明。最棘手的部分是弄清楚如何添加具有自定义类型的应用程序设置。我们从类型下拉列表中选择"浏览...",然后手动输入名称空间和类名称。项目中的类型不会显示在列表中。
更新:我添加了一些静态方法来简化添加到每个表单中的样板代码。遵循将WindowSettings类添加到项目并创建应用程序设置的说明后,下面是一个示例代码,该代码必须添加到要记录和恢复其位置的每种表单中。
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; }
请记住,重新编译会擦除配置文件中存储设置的位置,因此请对其进行测试,而无需在保存和还原之间进行代码更改。
下面的示例显示了我的操作方法
- 关闭表单时会调用SavePreferences并保存表单的大小,并带有一个标志,指示其是否已最大化(在此版本中,如果已最小化,则不会保存-下次会恢复或者最大化)。
- 从OnLoad调用LoadPreferences。
- 首先保存设计时的WindowState并将其设置为Normal。如果WindowState为Normal,则只能成功设置表单大小。
- 接下来,从持久设置中恢复"大小"。
- 现在,确保表单适合屏幕(调用FitToScreen)。自我们上次运行该应用程序以来,屏幕分辨率可能已更改。
- 最后,将WindowState设置回最大化(如果这样持久化),或者设置为之前保存的设计时值。
很显然,这可以适应于保持起始位置,并且在关闭时是否最小化了表单,我不需要这样做。表单上控件的其他设置(如拆分器位置和标签容器)很简单。
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