2008-09-19 105 views
23

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

  • 窗口位置相同,因为它是
    • 除非屏幕已调整到现在的老位置是关闭屏幕。
  • 分配器应保留其位置
  • 标签的容器应保持它们的选择
  • 一些下拉菜单应保留其选择
  • 窗口状态(最大化,最小化,正常)是因为它是相同的。
    • 也许你永远不应该开始最小化,我还没有决定。

我要添加当前的解决方案与局限性一起回答。

回答

18

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

缺点:

  • 更多的代码来写。
  • 很费力。您在formLoad上设置属性的顺序令人困惑。例如,您必须确保在设置分离器距离之前设置了窗口大小。

现在,这是我的首选解决方案,但它似乎太多的工作。为了减少工作量,我创建了一个WindowSettings类,它将窗口位置,大小,状态和任何分离器位置序列化为单个应用程序设置。然后,我可以为我的应用程序中的每个表单创建一个该类型的设置,保存关闭,然后在加载时恢复。

我发布了the source code,包括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); 
    } 
+0

伟大的示例代码! – 2009-04-01 17:07:44

+1

不幸的是,样本绑定的donkirkby项目的许可证可能不允许简单的自由使用代码。考虑在这里重新发布它。 – 2009-04-07 07:56:17

+0

我现在已经更改为MIT许可证;我并不是想限制使用代码。当然,赞赏是值得赞赏的。代码有点长,可以在这里发布。 – 2009-04-08 00:12:28

5

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

缺点:

  • 如果关闭该窗口最小化时,它会打开隐藏的下一次。真的很难把窗户拿回来。
  • 如果在最大化的情况下关闭窗口,它会打开填满整个屏幕,但不会最大化(小问题)。
  • 使用右上角或左下角调整窗口大小只是丑陋的。我猜这两个数据绑定属性正在互相攻击。

如果您想尝试这种奇怪的行为,我使用这种技术发布了一个sample solution

+0

有一个WindowState属性可以引用来检查最小化或最大化的窗口。但是,我不能评论窗口大小在两种状态之一时的情况。 – 2008-09-19 22:13:06

+0

是的,我尝试了WindowState属性。它表现得很奇怪。也许类似的问题是调整大小,同时更改多个数据绑定属性会导致争用和闪烁。 – 2008-09-19 22:18:59

2

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

2

您可以使用应用程序设置来设置该控件属性将被保留,在Form_closed事件中,你必须使用保存方法的应用程序设置写这些磁盘:

Properties.Settings.Default.Save(); 
2

这是我自己使用的一些例子。它只考虑主监视器,因此如果在多个监视器上使用它可能会更好地处理它。

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

我为我要保存的每个值的设定和使用这样的代码:

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

请记住,重新编译湿巾,其中存储的设置配置文件,所以测试而无需在保存和恢复之间进行代码更改。

6

下面的示例显示了如何做到这一点

  • SavePreferences收盘时的形式和保存形式的大小,如果它最大化指示标志(在这个版本我不保存,如果它被称为最小化 - 下次恢复或最大化)。

  • LoadPreferences从OnLoad调用。

  • 首先保存设计时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

3

基于由唐·柯比接受的答案和WindowSettings类,他写道,你可以从标准的一个派生CustomForm减少相同的代码为每个形式写的量,也许是这样的:

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_Closing例程中可能不是最佳选择,只要主应用程序退出,就可以将其保存并保存设置文件。

要使用它作为常规形式,只需使用无参数构造函数即可。

相关问题