開發視窗程式時,常常會需要記住視窗最後的位置,方便使用者下次開啟時,位置和上次相同,不需要再重新拖拉視窗
一般的做法是,在離開的時候才儲存位置,正常這樣的做法是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;
}
看起來是不是很簡單呢,相同的原理其實也可以用在其他地方,像是視窗大小拖拉結束等這類只有移動時才有事件的動作
參考來源
發表迴響