Я хочу создать дочернее окно с поддержкой dpi внутри окна WPF. Дочернее окно будет использоваться для визуализации DirectX.
Я создал минимальный пример с окном следующим образом:
<Window ...
Loaded="OnLoaded"
MouseMove="MainWindow_OnMouseMove">
<DockPanel>
<Menu DockPanel.Dock="Top">
<MenuItem Header="TestItem"/>
</Menu>
<Border Background="Blue" x:Name="BorderHost"/>
</DockPanel>
</Window>
Граница будет использоваться для размещения моего дочернего окна. HwndHost
для границы создает цепочку обмена DirectX и выглядит следующим образом:
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.
Примечание:
- Я не могу создать устройство Directx и цепочку обмена в один раз, потому что мне нужно работать с уже существующим устройством DirectX. Таким образом, отложенная инициализация цепочки обмена.
- У меня есть два монитора с разным разрешением, и я хочу, чтобы мое приложение правильно отображалось на обоих устройствах.
Для добавления точек на дюйм Осознавая свое приложение, я добавил манифест с:
<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 монитора равен 1,0:
Однако положение и размер дочернего окна на моем мониторе неверны с DPI 2.0:
Как правильно масштабировать и позиционировать мое дочернее окно?
Редактировать: Это то, что я получаю, когда не использую масштабирование gdiScaling внутри манифеста и перемещаю окно из 2,0 т / д на экран с разрешением 1,0 т / д: Окно выглядит хорошо на экране с разрешением 2,0 т / д, но заголовок моего окна теперь слишком велик на моем экране с разрешением 1,0 т / д.