在 WPF 開發中,當視窗樣式設為 WindowStyle=None(自訂標題列、無邊框)並最大化時,視窗會佔滿整個螢幕並遮蓋 Windows 工作列,這違反使用者體驗慣例。本文提供一個成熟、可直接復用的靜態工具類 FullScreenManager,透過攔截 WM_GETMINMAXINFO (0x0024) 視窗訊息,精確計算工作區域並修正最大化尺寸與位置。
WPF 預設最大化邏輯以螢幕解析度為準,忽略工作列佔用的區域。當 WindowStyle=None 時,系統不再自動處理工作列避讓,導致視窗覆蓋任務列。
掛鉤 HwndSource 的 WindowProc,攔截 WM_GETMINMAXINFO 訊息:
private static IntPtr WindowProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) {
switch (msg) {
case 0x0024: // WM_GETMINMAXINFO
WmGetMinMaxInfo(hwnd, lParam);
handled = true;
break;
}
return IntPtr.Zero;
}
呼叫 Win32 API 取得監視器資訊,將最大化位置與尺寸限制在工作區域(排除工作列):
private static void WmGetMinMaxInfo(IntPtr hwnd, IntPtr lParam) {
var mmi = (MINMAXINFO)Marshal.PtrToStructure(lParam, typeof(MINMAXINFO));
int MONITOR_DEFAULTTONEAREST = 0x00000002;
IntPtr monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
if (monitor != IntPtr.Zero) {
var monitorInfo = new MONITORINFO();
GetMonitorInfo(monitor, monitorInfo);
RECT rcWork = monitorInfo.rcWork; // 工作區域(不含工作列)
RECT rcMonitor = monitorInfo.rcMonitor; // 整個監視器區域
mmi.ptMaxPosition.x = Math.Abs(rcWork.left - rcMonitor.left);
mmi.ptMaxPosition.y = Math.Abs(rcWork.top - rcMonitor.top);
mmi.ptMaxSize.x = Math.Abs(rcWork.right - rcWork.left);
mmi.ptMaxSize.y = Math.Abs(rcWork.bottom - rcWork.top);
}
Marshal.StructureToPtr(mmi, lParam, true);
}
包含 MINMAXINFO、MONITORINFO、POINT、RECT 結構與 MonitorFromWindow、GetMonitorInfo 函式宣告,確保跨版本相容。
在視窗建構函式或 Loaded 事件中呼叫:
public MainWindow() {
InitializeComponent();
FullScreenManager.RepairWpfWindowFullScreenBehavior(this);
}
工具類會自動處理:
文末附上完整訊息代碼表(依名稱與代碼雙向排序),方便開發者查閱 WM_* 常數值,如 WM_PAINT=0x000F、WM_SIZE=0x0005、WM_MOUSEMOVE=0x0200 等。