2008-09-19 235 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例程中可能不是最佳選擇,只要主應用程序退出,就可以將其保存並保存設置文件。

要使用它作爲常規形式,只需使用無參數構造函數即可。

相關問題