簡體   English   中英

C# WPF 創建 dpi 感知子窗口

[英]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上找到。

筆記:

  • 我無法同時創建 Directx 設備和交換鏈,因為我需要使用現有的 Directx 設備。 因此延遲交換鏈初始化。
  • 我有兩台 DPI 不同的顯示器,我希望我的應用程序能夠在兩台設備上正確顯示。

為了向我的應用程序添加 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 為 1.0 時,窗口看起來不錯: 美好的

但是,使用 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM