開發視窗程式時,常常會需要記住視窗最後的位置,方便使用者下次開啟時,位置和上次相同,不需要再重新拖拉視窗
一般的做法是,在離開的時候才儲存位置,正常這樣的做法是OK的,不過我的狀況有點特殊,使用者可能不會乖乖地離開,狀況不一定,所以我希望在視窗拖拉結束時儲存位置
WPF有提供LocationChanged事件,讓你可以視窗拖拉時觸發,不過卻不曉得何時拖拉會結束,有時候系統沒提供事件就很難處理,必須自己用較消耗資源的方式實現
在google過後發現stackoverflow上有一篇和我相同的問題,WPF Window LocationChanged ended,其中最佳解答只提供了做法描述,並沒有實際執行的程式碼可以參考,如果我還是新手時,可能就直接關掉視窗另尋他路了吧,不過仔細看過其實實作不難,它提供了兩種做法
- 設置一個timer,間隔幾百毫秒,在第一次LocationChanged事件時讓timer開始計時,之後LocationChanged就重新計時,不讓timer觸發事件,等到timer觸發時,就可以假設使用者已經停止拖拉了,缺點是需要額外一個timer消耗資源
- 使用window message hook來監聽WM_NCLBUTTONUP事件
第2種做法看起來比較好,不過實作後發現沒法偵測到WM_NCLBUTTONUP,google後似乎有許多種原因,就暫不深究,採用第1種方式,雖然要一個timer,但timer實際運作時間也只有拖拉時和放開後的幾百毫秒,以下就來說明怎麼做吧
首先在window上添加LocationChanged事件: Window_LocationChanged
<Window x:Class="GateControl.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:GateControl" Title="GateControl" Width="800" Height="600" LocationChanged="Window_LocationChanged"></Window>
接著設置timer和判斷用的變數,在程式開始時,讀取並設置視窗位置
//判斷視窗是否正在移動 bool isMovingWindow = false; Timer timerSaveWindowPosition; public MainWindow() { InitializeComponent(); //如果在window的Loaded事件才寫入,會看到視窗從舊位置跳到新位置 Left = Properties.Settings.Default.left; Top = Properties.Settings.Default.top; timerSaveWindowPosition = new Timer(500); timerSaveWindowPosition.Elapsed += (o, a) => { //放開拖拉後,會先將isMovingWindow寫入false,之後才會觸發儲存視窗位置 if (!isMovingWindow) { Dispatcher.BeginInvoke(new Action(() => { Properties.Settings.Default.left = Left; Properties.Settings.Default.top = Top; Properties.Settings.Default.Save(); })); Console.WriteLine("save position"); //任務完成,記得停用timer,避免一直儲存消耗資源 timerSaveWindowPosition.Stop(); } //即使移動中觸發了timer,還是被改寫成true,只有在靜止時,才會停留在false isMovingWindow = false; } } private void Window_LocationChanged(object sender, EventArgs e) { //由靜止開始移動時,讓timer開始計時 if (!isMovingWindow) { timerSaveWindowPosition.Start(); } //寫入視窗正在移動 isMovingWindow = true; }
看起來是不是很簡單呢,相同的原理其實也可以用在其他地方,像是視窗大小拖拉結束等這類只有移動時才有事件的動作
參考來源
發表迴響