设置两个窗口的父子关系非常简单,只需要调用 SetParent
函数即可。然而设置两个窗口的所有者(Owner)关系却没有一个简单直观的 API。那么本文介绍一下如何设置两个窗口的 Owner 关系。
设置所有者(Owner)
由于方法非常简单,所以我直接贴出 MainWindow
中的完整代码:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
SourceInitialized = OnSourceInitialized;
}
private void OnSourceInitialized(object? sender, EventArgs e)
{
var ownerHwnd = User32.GetForegroundWindow();
var hwnd = new WindowInteropHelper(this).Handle;
User32.SetWindowLong(hwnd,
GetWindowLongIndexes.GWL_HWNDPARENT,
(nint)ownerHwnd);
}
}
在这里,我准备好了两个窗口句柄,一个是 ownerHwnd
,我随便取了当前窗口的;另一个是 hwnd
即自己的句柄。这样,程序启动的时候,便会把自己窗口的所有者设置为启动前最后一个前台窗口。
接下来是关键代码 SetWindowLong
,传入三个参数:
- 自己窗口的句柄
hwnd
GWL_HWNDPARENT
即指定所有者(在官方文档中,依然将其描述为 parent`)- 所有者窗口的句柄
ownerHwnd
所需 API
在 C# 中,以上 API 函数需要定义。为了方便,你可以直接安装库 Lsj.Util.Win32 以省去所有的定义工作。
如果你不想引入库,可以用下面我准备好的定义(摘自 Lsj.Util.Win32 并简化):
代码语言:javascript复制public static nint SetWindowLong([In] nint hWnd, [In] GetWindowLongIndexes nIndex, [In] nint dwNewLong) => nint.Size > 4
? SetWindowLongPtrImp(hWnd, nIndex, dwNewLong)
: SetWindowLongImp(hWnd, nIndex, dwNewLong.ToInt32());
[DllImport("user32.dll", CharSet = CharSet.Unicode, EntryPoint = "SetWindowLongW", ExactSpelling = true, SetLastError = true)]
private static extern int SetWindowLongImp(nint hWnd, GetWindowLongIndexes nIndex, int dwNewLong);
[DllImport("user32.dll", CharSet = CharSet.Unicode, EntryPoint = "SetWindowLongPtrW", ExactSpelling = true, SetLastError = true)]
private static extern nint SetWindowLongPtrImp(nint hWnd, GetWindowLongIndexes nIndex, nint dwNewLong);
public enum GetWindowLongIndexes
{
GWL_HWNDPARENT = -8,
}
后续需求
出于兼容性考虑,即便设置为了所有者关系,Windows 系统也不会强制修改窗口的样式(例如从任务栏中去掉)。你可以考虑将窗口的 WindowStylesEx
属性中的 WS_EX_APPWINDOW
部分去掉来实现这样的效果。
var style = style & ~WindowStylesEx.WS_EX_APPWINDOW;
至于具体如何使用 GetWindowLong
和 SetWindowLong
来实现以上目的,本文就不赘述了。
参考资料
- SetParent function (winuser.h) - Win32 apps - Microsoft Learn
- GetWindowLongA function (winuser.h) - Win32 apps - Microsoft Learn
- winapi - How to change a Window Owner using its handle - Stack Overflow
本文会经常更新,请阅读原文: https://cloud.tencent.com/developer/article/2350170 ,以避免陈旧错误知识的误导,同时有更好的阅读体验。
本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名 吕毅 (包含链接: https://blog.walterlv.com ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请 与我联系 ([email protected]) 。