C# WPF создает дочернее окно с поддержкой dpi - PullRequest
1 голос
/ 05 января 2020

Я хочу создать дочернее окно с поддержкой 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: fine

Однако положение и размер дочернего окна на моем мониторе неверны с DPI 2.0: not_fine

Как правильно масштабировать и позиционировать мое дочернее окно?


Редактировать: Это то, что я получаю, когда не использую масштабирование gdiScaling внутри манифеста и перемещаю окно из 2,0 т / д на экран с разрешением 1,0 т / д: still_broken Окно выглядит хорошо на экране с разрешением 2,0 т / д, но заголовок моего окна теперь слишком велик на моем экране с разрешением 1,0 т / д.

1 Ответ

0 голосов
/ 10 января 2020

Согласно документу , я изменил 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>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...