[英]C# WPF create dpi aware child window
我想在 WPF 窗口内创建一个 dpi 感知子窗口。 子窗口将用于 DirectX 渲染。
我创建了一个带有窗口的最小示例,如下所示:
<Window ...
Loaded="OnLoaded"
MouseMove="MainWindow_OnMouseMove">
<DockPanel>
<Menu DockPanel.Dock="Top">
<MenuItem Header="TestItem"/>
</Menu>
<Border Background="Blue" x:Name="BorderHost"/>
</DockPanel>
</Window>
Border 将用于托管我的子窗口。 边界的HwndHost
创建HwndHost
交换链,如下所示:
public class ChildWindow : HwndHost
{
private readonly Border parent;
private IntPtr hWnd = IntPtr.Zero;
public SwapChain SwapChain { get; private set; }
public ChildWindow(Border parent)
{
this.parent = parent;
parent.SizeChanged += ParentOnSizeChanged;
}
private void ParentOnSizeChanged(object sender, SizeChangedEventArgs e)
{
// problem: width and height is not correctly scaled
SwapChain?.Resize((int)(parent.ActualWidth), (int)(parent.ActualHeight));
}
protected override HandleRef BuildWindowCore(HandleRef hwndParent)
{
// create subwindow
hWnd = CreateWindowEx(
0, // dwstyle
"static", // class name
"", // window name
WS_CHILD | WS_VISIBLE, // style
0, // x
0, // y
(int)parent.ActualWidth, // renderWidth
(int)parent.ActualHeight, // renderHeight
hwndParent.Handle, // parent handle
IntPtr.Zero, // menu
IntPtr.Zero, // hInstance
0 // param
);
// directx swap chain
// problem: width and height is not correctly scaled
SwapChain = new SwapChain(hWnd, (int)parent.ActualWidth, (int)parent.ActualHeight);
return new HandleRef(this, hWnd);
}
protected override void DestroyWindowCore(HandleRef hwnd)
{
DestroyWindow(hWnd);
}
[DllImport("user32.dll", EntryPoint = "DestroyWindow", CharSet = CharSet.Unicode)]
internal static extern bool DestroyWindow(IntPtr hwnd);
[DllImport("user32.dll", EntryPoint = "CreateWindowEx", CharSet = CharSet.Unicode)]
internal static extern IntPtr CreateWindowEx(
int dwExStyle,
string lpszClassName,
string lpszWindowName,
int style,
int x, int y,
int width, int height,
IntPtr hwndParent,
IntPtr hMenu,
IntPtr hInst,
[MarshalAs(UnmanagedType.AsAny)] object pvParam
);
internal const int
WS_CHILD = 0x40000000,
WS_VISIBLE = 0x10000000;
}
主窗口初始化子窗口并在鼠标移动后将其背景清除为黑色或白色:
public partial class MainWindow : Window
{
private ChildWindow child;
private float childColor = 1.0f;
public MainWindow()
{
InitializeComponent();
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
child = new ChildWindow(BorderHost);
BorderHost.Child = child;
}
private void MainWindow_OnMouseMove(object sender, MouseEventArgs e)
{
if (child == null) return;
if (child.SwapChain == null) return;
// switch between black and white background
childColor = 1.0f - childColor;
// clear child background
child.SwapChain.BeginFrame();
Device.Get().ClearRenderTargetView(child.SwapChain.Rtv, new RawColor4(childColor, childColor, childColor, childColor));
child.SwapChain.EndFrame();
}
}
完整的源代码可以在https://github.com/kopaka1822/DpiAwareChildwindow上找到。
笔记:
为了向我的应用程序添加 dpi 感知,我添加了一个清单:
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">True/PM</dpiAware>
<gdiScaling xmlns="http://schemas.microsoft.com/SMI/2017/WindowsSettings">true</gdiScaling>
</windowsSettings>
</application>
我将gdiScaling
设置为 true,因为我希望窗口内的所有 WPF 组件都能像往常一样缩放。
但是,使用 DPI 2.0 的显示器上的子窗口位置和大小是错误的:
如何正确缩放和定位我的子窗口?
编辑:这就是我在清单中不使用 gdiScaling 缩放并将窗口从 2.0 dpi 移动到 1.0 dpi 屏幕时得到的结果: 该窗口在 2.0 dpi 屏幕上看起来不错,但我的窗口的标题栏现在在 1.0 dpi 屏幕上过大。
根据文档,我将gdiScaling
修改为以下格式,它对我gdiScaling
。
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/pm</dpiAware>
<!--<gdiScaling xmlns="http://schemas.microsoft.com/SMI/2017/WindowsSettings">true</gdiScaling>-->
</windowsSettings>
</application>
<application>
<windowsSettings xmlns="https://schemas.microsoft.com/SMI/2017/WindowsSettings">
<gdiScaling>true</gdiScaling>
</windowsSettings>
</application>
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.