WPF 已知問題 使用 WindowChrome 在切換系統主題色時導致窗口界面偏移
此問題一開始是 Office Tool Plus 的作者 Yerong 在 https://github.com/dotnet/wpf/issues/11204 報告給我的問題。我著手調查此問題時,沒有去翻歷史記錄。于是調查路徑走偏。后面在 Michael Dietrich 大佬的糾偏下,我才回憶起在 2020 那會,同是 WPF 開源倉庫成員的 Bastian Schmidt 大佬在 https://github.com/dotnet/wpf/issues/3193 處理過相關的問題。Bastian Schmidt 大佬就是 Snoop 工具的作者
此問題不能全說是 WPF 的問題,系統層也存在一個設計如此的問題。只是在 WPF 里面也有一個鍋,那就是在 https://github.com/dotnet/wpf/blob/808031c2d49fd49ece87fb1e27d9c0774f30aa6b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Shell/WindowChromeWorker.cs#L445 這里面,只是返回 WVR.REDRAW 重新渲染,而沒有帶上 WVR_VALIDRECTS 需要刷新臟區的
由于 WPF 需要考慮的范圍太多,給 WPF 疊加這個改動估計沒人敢合并,因為有啥副作用是難以快速測試出來的,影響面過廣。鑒于此 Bastian Schmidt 大佬就推薦使用 https://github.com/ControlzEx/ControlzEx 庫。我現在自己的項目或團隊的項目的 WindowChrome 也都在用 https://github.com/ControlzEx/ControlzEx 或 https://github.com/dotnet-campus/dotnetCampus.Windows7.Shell 的實現
我認為對此問題的最佳解決方法是換成 https://github.com/ControlzEx/ControlzEx 庫,或者是取出其實現代碼抄過來
如果不想使用庫,那也可以使用 Michael Dietrich 大佬 提供的方法:自己給窗口帶上鉤子,當收到 WM_NCPAINT 或 WM_SETTINGCHANGE 消息時,則調用 InvalidateRect 通知系統渲染層刷新整個窗口界面。其核心代碼如下
public MainWindow()
{
InitializeComponent();
SourceInitialized += OnSourceInitialized;
}
private void OnSourceInitialized(object? sender, EventArgs e)
{
var windowInteropHelper = new WindowInteropHelper(this);
var hwnd = windowInteropHelper.Handle;
HwndSource source = HwndSource.FromHwnd(hwnd)!;
source.AddHook(Hook);
}
private IntPtr Hook(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam, ref bool handled)
{
const int WM_NCPAINT = 0x0085;
const int WM_SETTINGCHANGE = 0x001A;
if (msg is WM_NCPAINT or WM_SETTINGCHANGE)
{
Window window = this;
var rect = new Int32Rect(0,0, (int)Math.Ceiling(window.ActualWidth), (int) Math.Ceiling(window.ActualHeight));
unsafe
{
InvalidateRect(hwnd, &rect, true);
}
}
return IntPtr.Zero;
}
[DllImport("User32")]
private static extern unsafe bool InvalidateRect(IntPtr hwnd, Int32Rect* lpRect, bool bErase);
以上代碼放在 github 和 gitee 上,可以使用如下命令行拉取代碼。我整個代碼倉庫比較龐大,使用以下命令行可以進行部分拉取,拉取速度比較快
先創建一個空文件夾,接著使用命令行 cd 命令進入此空文件夾,在命令行里面輸入以下代碼,即可獲取到本文的代碼
git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin 9bc7db74f69823d0c0d762492dea640c8048d67c
以上使用的是國內的 gitee 的源,如果 gitee 不能訪問,請替換為 github 的源。請在命令行繼續輸入以下代碼,將 gitee 源換成 github 源進行拉取代碼。如果依然拉取不到代碼,可以發郵件向我要代碼
git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git
git pull origin 9bc7db74f69823d0c0d762492dea640c8048d67c
獲取代碼之后,進入 WPFDemo/LellaibeayeelaRakearjemfal 文件夾,即可獲取到源代碼
更多技術博客,請參閱 博客導航
博客園博客只做備份,博客發布就不再更新,如果想看最新博客,請訪問 https://blog.lindexi.com/
如圖片看不見,請在瀏覽器開啟不安全http內容兼容

本作品采用知識共享署名-非商業性使用-相同方式共享 4.0 國際許可協議進行許可。歡迎轉載、使用、重新發布,但務必保留文章署名[林德熙](http://www.rzrgm.cn/lindexi)(包含鏈接:http://www.rzrgm.cn/lindexi ),不得用于商業目的,基于本文修改后的作品務必以相同的許可發布。如有任何疑問,請與我[聯系](mailto:lindexi_gd@163.com)。

浙公網安備 33010602011771號