I want to create a dpi aware child window inside a WPF window. The child window will be used for directX rendering.
I created a minimal example with the window as follows:
<Window ...
Loaded="OnLoaded"
MouseMove="MainWindow_OnMouseMove">
<DockPanel>
<Menu DockPanel.Dock="Top">
<MenuItem Header="TestItem"/>
</Menu>
<Border Background="Blue" x:Name="BorderHost"/>
</DockPanel>
</Window>
The Border will be used to host my child window. The HwndHost
for the border creates the directX swap chain and looks like this:
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;
}
The main window intializes the child window and clears its background to black or white after a mouse move:
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();
}
}
The full source code can be found on https://github.com/kopaka1822/DpiAwareChildwindow.
Note:
In order to add dpi awareness to my app, I added a manifest with:
<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>
I set gdiScaling
to true, because I want all WPF components inside the window to scale as usual.
The window looks fine when the monitor DPI is 1.0:
However, the child window position and size is wrong on my monitor with DPI 2.0:
How do I properly scale and position my child window?
Edit: This is what I get when I don't use the gdiScaling scaling inside the manifest and move my window from the 2.0 dpi to my 1.0 dpi screen: The window looks fine on the 2.0 dpi screen but the title bar of my window is now over sized on my 1.0 dpi screen.
According to the document, I modified gdiScaling
to the following format, and it work for me.
<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>
User contributions licensed under CC BY-SA 3.0