一、什么是空域
- 在顶层窗口中,您可以将每个包含互操作应用程序的技术之一的HWND概念化为有自己的“空间”。窗口内的每个像素恰好属于一个HWND,这构成了该HWND的空域。(严格来说,如果有一个以上的WPF高速公路,将会有一个以上的WPF空域,但是为了解释这个概念,假设在本主题给出的例子中只有一个)。空域概念意味着,在应用程序生命周期内,所有试图在该像素之上进行渲染的图层或其他窗口都必须属于同一互操作技术。试图在Win32上呈现WPF像素会导致不希望的结果,并且通过互操作API尽可能不被允许(这段话是抄的啦)这里有介绍,简单的来说就是不同的渲染技术导致了空域的产生,最常见现象,wpf 上放一个winform控件,你会发现winform控件悬浮于wpf 控件上方,或者设置AllowsTransparency = true 你使用的winform控件会透明 很蛋疼
二、我遇到空域问题
- 之前有个客户要做视频解决方案,要求是要在多个视频窗口上贴上标签,比如人员名称等,但是由于空域问题,导致贴图没有显示,贼烦人
三、我尝试解决办法
1.Microsoft.DwayneNeed https://microsoftdwayneneed.codeplex.com/ 怎么说呢 ,这个库我个人没觉得有多好用,因为视频小窗口特别多,用这个巨卡无比
2.尝试使用winform来包含wpf控件贴上去,这种也是可以,但是及不稳定,性能也极差无比,卡顿 拖影出现频率很高
3.尝试用popup来解决,但是单个视频窗口还行,如果多个视频窗口加载界面,不知为何总有个别popup弹不出来(未找到原因)
4.某视频软件sdk提供的demo,用window弹出界面show到指定位置,然后实时计算位置,这个方法可以实现,但是因为视频界面最多有十一个视频画面,每个画面有标题和控制面板两个部分,就是需要弹出20个windows,控制起来非常繁琐
5.方法4虽然没有完全解决我的问题,但是至少给我很大启发,仔细研究demo以及查阅资料,想到有没有一种办法,我把wpf window 作为一个usercontrol嵌入到wpf中,查阅官方文档发现一个HwndHost类,查阅官方文档这个类描述为将 Win32 window 托管为 Windows Presentation Foundation (WPF) 内容中的一个元素。这就很开心了,故开心采用此方法
四、解决方法的过程
HwndHost 是一个抽象类,如果要实现窗口托管,需要自己实现一个子类,如下:
public class THost : HwndHost
{
public const int GWL_STYLE = (-16);
public const int WS_CHILD = 0x40000000;
[DllImport("user32.dll")]
public static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
[DllImport("user32.dll")]
static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern int SetWindowPos(IntPtr hWnd, int hWndInsertAfter, int x, int y, int Width, int Height, int flags);
private IntPtr ChildHandle = IntPtr.Zero;
private IntPtr ParentHandle = IntPtr.Zero;
private Window _window;
public THost(Window window, IntPtr parentPtr)
{
_window = window;
this.ChildHandle = (new WindowInteropHelper(_window)).Handle;
//LogHelper.Write($"生成窗体------------{_window.Title}");
ParentHandle = parentPtr;
}
[HandleProcessCorruptedStateExceptions]
protected override HandleRef BuildWindowCore(HandleRef hwndParent)
{
HandleRef href = new HandleRef();
if (ChildHandle != IntPtr.Zero)
{
SetWindowLong(this.ChildHandle, GWL_STYLE, WS_CHILD);
SetParent(this.ChildHandle, hwndParent.Handle);
href = new HandleRef(Application.Current.MainWindow, this.ChildHandle);
}
return href;
}
protected override void DestroyWindowCore(HandleRef hwnd)
{
_window = null;
this.Dispose();
}
}
主要重写方法BuildWindowCore 和方法DestroyWindowCore 传入参数即为父类窗口handle ,wpf 传入mainwindow的handle即可,然后还有你弹出的window 的handle,这样基本可以完美解决,
五、总结
你以为这样就完事儿?微软的尿性告诉我没有这么简单,当我开开心心,去用户机器上尝试,发现卧槽 居然不行,,仔细一看win7,这可要了我老命,win10下完美运行拖动跟随都没有问题,win7不可以,经过漫长的解决方案查找,突然想起win7和win10区别,areo特效。。。,司马当做活马医,将用户桌面改成basic主题,可是还是不行,直到有一天,我想看看我拖动窗口他位置什么情况,因为之前弹出窗口一直设置了AllowsTransparency = true,这次我去掉它,卧槽 居然可以拖动跟随并且跟win10下基本一样,区别必须给你要传入的窗口设置一个颜色,
六、最后
win10情况下使用此方法基本没有问题
win7下需要特殊处理,首先不能应用areo效果,其次需要给嵌入的窗口设置一个背景色
这是我目前遇到的情况,希望可以给大家一些帮助,或者大家有更好的解决方案,也可以通过公众号发给我哦