[WinForm] 記住程式視窗的最後位置和其它偏好設定

有些程式可以記住它們的最後位置, 並且在下次開啟時自動開啟在上次所在的位置。不管使用者把程式視窗搬到哪個地方, 下一次再被開啟時就會自動開啟在那個地方。這是怎麼辦到的...

有些程式可以記住它們的最後位置, 並且在下次開啟時自動開啟在上次所在的位置。不管使用者把程式視窗搬到哪個地方, 下一次再被開啟時就會自動開啟在那個地方。這是怎麼辦到的?

首先, 我們可以在專案中增加一個資源項目, 並給予一個 Key。請先從 Solution Explorer 裡面, 專案名稱上面按下滑鼠右鍵, 選擇 Properties, 進入專案設定畫面, 然後在 Settings 頁籤中, 加入一個名稱, 例如 "frmPosition", 其 Type 是 "System.Drawing.Point, Scope 是 "User", Value 則暫時給予 "0, 0"。存檔。

接著, 在 Form 的 Design View 裡面的 Properties 視窗裡面, 找到 "(ApplicationSettings)" 一節 (如果是按字母順序排列的話, 應該在最上面), 在裡面找到 Location 項目, 然後在右邊下拉式選單中找到 frmPosition 這一項。

現在, 同樣在 Form 的 Properties 視窗中的上方, 選擇閃電符號以進入事件列表。找到 FormClosing 事件, 在方框中按兩下滑鼠鍵, 讓 Visual Studio 建立並前往事件處理函式 (XXX_FormClosing() 方法)。並在裡面加入以下程式:

Properties.Settings mySettings = new [你的 Namespace].Properties.Settings(); 
mySettings.frmPosition = new Point(this.Location.X, this.Location.Y); 
mySettings.Save();

這樣就大功告成了。

運用相同的做法, 除了視窗的最後位置之外, 你其實也可以記錄其它各種使用者的偏好設定 (例如視窗大小、字型等等)。說穿了, 就等於是 ASP.NET 裡面的 Profile 機制, 只不過在使用上比 Profile 還要簡單許多。你可以從 Properties 視窗裡的 (ApplicationSettings) 一節中找到 (PropertyBinding) 項目, 把內容打開, 我相信你可以從長串列表中尋找到許多靈感。

其實你也不一定要將這類設定值寫在專案的 Properties 裡面, 記錄在文字檔或資料庫裡面也沒什麼不可以。只是 Visual Studio 在其介面中只認得設定在專案的 Properties 的資源。如果你一定要將值記錄在其它地方, 你就必須記得以手動方式在 Load 事件中把值讀出來, 並把 Form 的 Location 予以設定, 然後在 FormClosing 事件中把值寫入。

例如, 假設你使用 INI 檔案儲存設定(請參考「讀寫 INI 檔案」一文), 那麼你的程式可以這麼寫:

private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
    iniAccess ia = new iniAccess();
    int x = this.Location.X;
    int y = this.Location.Y;
    string loc = x.ToString() + "," + y.ToString();
    ia.write("General", "FormPosition", loc);            
}

private void Form1_Load(object sender, EventArgs e)
{
    iniAccess ia = new iniAccess();
    string originalPosition = ia.read("General", "FormPosition");
    if (!string.IsNullOrEmpty(originalPosition))
    {
        string[] pos = originalPosition.Split(',');
        int x = 0, y = 0;
        int.TryParse(pos[0], out x);
        int.TryParse(pos[1], out y);
        if (x > 0 & y > 0)
            this.Location = new Point(x, y);
    }
}

如此, 你在程式結束時會把 Location 記錄在 INI 檔案中, 且按照「讀寫 INI 檔案」文章中的方法, 這個 .ini 檔案可以不需要事先建立, 它在第一次寫入時會自動建好。等你下次再執行程式時, 它會從這個 .ini 檔案中把位置取回, 並回到設定的位置; 如果沒有這個檔案或數值不對, 那麼它會定位在預設的位置(在表單的 StartPosition 屬性中決定; 例如你可以將它設定為 CenterScreen, 那麼它會自動定位在畫面的正中間)。

此外, 有鑒於現在雙重/多重螢幕流行, 如果使用者把視窗拉到其它螢幕, 那麼我們記錄的座標會超過主螢幕的範圍。但是, 如果使用者下次開啟程式時並沒有連接上雙螢幕 (這種情形時常出現在以筆記型電腦連接投影機的情況下), 那麼程式視窗就消失了! 當然視窗並沒有消失, 但是因為它的所在座標超出主螢幕的顯示範圍, 所以我們看不見。

有經驗的使用者也許知道可以使用 Window + 左右鍵把視窗拉回來(但是其作業系統也必須是 Windows 7)。但這畢竟不是最好的方法, 因為絕大多數使用者會以為是程式開不起來。

那麼, 我們究竟怎麼以程式方式把它拉回來?

在回答這個問題之前, 我們必須得先知道如何判斷系統有沒有連上多重螢幕。.Net 提供了一個非常簡單的方法, 可以告訴我們現在系統上接了幾個螢幕:

/// <summary>
/// Detect the system and determin whether the system equips with more than 1 screen
/// </summary>
public static bool isDualMonitor()
{
    return Screen.AllScreens.Length > 1;
} 

所以只要讀取 System.Windows.Forms.Screen.AllScreens.Length 就可以知道使用者有沒有連上多重螢幕。

接著, 我們只要把原來在 Form.Load() 中重新定位視窗位置的程式稍加修改即可:

if ((x < 0 || x > Screen.PrimaryScreen.Bounds.Width ||
    y < 0 || y > Screen.PrimaryScreen.Bounds.Height) &&
    !isDualMonitor()) ;       // If the location from INI is outside the primary screen and it doesn't 
else                                 // equip with dual screens, the window location will not be restored.
    this.Location = new Point(x, y); // Otherwise the window location will be restored.

在上述程式中, 如果記錄的座標超出主要螢幕, 而且我們判斷出當前系統並未連上多重螢幕, 那麼如果此時我們強制把視窗座標還原, 使用者一定看不到該視窗; 所以在這種情況下, 我們就不強迫視窗定位在原來的地方, 而是讓它自動回歸預設的位置(例如主螢幕的正中間)。


Dev 2Share @ 點部落