【问题标题】:C# WPF & WinForms Interop - CenterOwner not effectiveC# WPF 和 WinForms 互操作 - CenterOwner 无效
【发布时间】:2023-03-04 18:50:01
【问题描述】:

我正在开发一个使用 WPF 和 WinForms 作为其 UI 的遗留应用程序。 WPF 占了绝大多数,但应用程序主对话框仍在 WinForms 中。

到目前为止,我已经能够让它们很好地协同工作(感谢 System.Windows.Forms.Integration.ElementHost),但我无法让 WPF 窗口以它们的 WinForms 父级为中心。

我的代码如下所示。

WPF 控件(托管在 ElementHost 中)

private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
    var dialog = new SubWindow();
    WindowOwnershipHelper.SetOwner(dialog, this);
    dialog.ShowDialog();
}

OwnershipHelper(取自https://stackoverflow.com/a/36606974/13567181

此类建立“父子”关系,以便嵌套对话框在同一屏幕上打开并与其父级一起最小化...

public static class WindowOwnershipHelper
{
    public static void SetOwner(Window window, Visual parent)
    {
        var source = (HwndSource) PresentationSource.FromVisual(parent);

        if (source == null)
        {
            throw new InvalidOperationException("Could not determine parent from visual.");
        }

        new WindowInteropHelper(window).Owner = source.Handle;
    }
}

我面临的问题是,当 dialog.ShowDialog() 执行时,新打开的窗口根本不在其所有者周围。它在屏幕上的某个地方,但我不太明白它是如何确定其位置的。

有趣的是,如果我再次在 SubWindow 类中重复 ButtonBase_OnClick 代码,这个新窗口将完全围绕它的 SubWindow 父窗口居中。

在我看来,这与 SubWindowElementHost 父级有关。

有人可以告诉我如何在不手动计算其位置的情况下让子窗口围绕其父窗口居中吗? (类似于这个https://stackoverflow.com/a/42401001/13567181

编辑:我刚刚在 MSDN 上找到了这个 - 它有点相似,但我不确定。 https://social.msdn.microsoft.com/Forums/vstudio/en-US/05768951-73cf-4daf-b369-0905ca7e5222/centering-wpf-window-on-winforms-owner-window?forum=wpf

最好的问候 诺伯特

【问题讨论】:

    标签: c# wpf winforms winforms-interop


    【解决方案1】:

    使用Application.Run(new MyForm2());,然后单击按钮创建一个以主窗体为中心的WPF Window

    public class MyForm2 : Form {
    
        public MyForm2() {
            this.Size = new Size(600,600);
            this.StartPosition = FormStartPosition.CenterScreen;
            Button btn = new Button { Text = "New Window", AutoSize = true, AutoSizeMode = AutoSizeMode.GrowAndShrink };
            btn.Click += btn_Click;
            Controls.Add(btn);
        }
    
        void btn_Click(object sender, EventArgs e) {
            var w = new System.Windows.Window();
            w.SourceInitialized += w_SourceInitialized;
            w.Width = 400.0; // number of actual pixels might be different
            w.Height = 400.0; // depending on DPI. My laptop is 120 dpi, so 400.0 -> 400 * 120 / 96 = 500 pixels.
            w.Title = "WPF Window";
            w.ShowDialog();
        }
    
        void w_SourceInitialized(object sender, EventArgs e) {
            var w = (System.Windows.Window) sender;
            WindowInteropHelper helper = new WindowInteropHelper(w);
            //w.WindowStartupLocation = System.Windows.WindowStartupLocation.CenterOwner; does nothing
            int GWL_HWNDPARENT = -8;
            SetWindowLongInternal(helper.Handle, GWL_HWNDPARENT, this.Handle);
    
            Rectangle r = this.Bounds;
            RECT r2 = new RECT();
            GetWindowRect(helper.Handle, out r2);
            int w2 = r2.Right - r2.Left;
            int h2 = r2.Bottom - r2.Top;
    
            int x2 = r.X + (r.Width - w2) / 2;
            int y2 = r.Y + (r.Height - h2) / 2;
    
            uint SWP_NOSIZE        = 0x0001;
            uint SWP_NOZORDER      = 0x0004;
            uint SWP_NOREDRAW      = 0x0008;
            uint SWP_NOACTIVATE    = 0x0010;
            uint SWP_NOCOPYBITS    = 0x0100;
            uint SWP_NOOWNERZORDER = 0x0200;
            uint flags = SWP_NOACTIVATE | SWP_NOCOPYBITS | SWP_NOOWNERZORDER | SWP_NOREDRAW | SWP_NOSIZE | SWP_NOZORDER;
    
            SetWindowPos(helper.Handle, IntPtr.Zero, x2, y2, 0, 0, flags);
        }
    
        [StructLayout(LayoutKind.Sequential)]
        private struct RECT {
            public int Left;
            public int Top;
            public int Right;
            public int Bottom;
        }
    
        [DllImport("user32.dll")]
        private static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);
    
        [DllImport("user32.dll", SetLastError=true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int w, int h, uint uFlags);
    
        [DllImport("user32.dll", SetLastError=true)]
        private static extern int SetWindowLong(IntPtr hWnd, int nIndex, IntPtr dwNewLong);
        [DllImport("user32.dll", SetLastError=true)]
        private static extern int SetWindowLongPtr(IntPtr hWnd, int nIndex, IntPtr dwNewLong);
    
        private static int SetWindowLongInternal(IntPtr hWnd, int nIndex, IntPtr dwNewLong) {
            if (IntPtr.Size == 4)
                return SetWindowLong(hWnd, nIndex, dwNewLong);
    
            return SetWindowLongPtr(hWnd, nIndex, dwNewLong);
        }
    }
    

    【讨论】:

    • 感谢您的剪辑 - 就像一个魅力。我正在尝试将您的解决方案与我的解决方案结合起来。为此,我创建了一个派生自System.Windows.Window 的类,并添加了一个名为SetOwner 的方法——这样我就可以掌握父窗口句柄。我正在努力解决的最后一件事是如何在不传递它的情况下获得this.Bounds。有没有办法从其窗口句柄中获取父级的边界?
    • 已经在代码里了,GetWindowRect(IntPtr hWnd, out RECT lpRect);
    • 使用GetWindowLongInternal 方法获取所有者窗口的句柄。另一个版本的代码已经发布。
    【解决方案2】:

    此版本添加了一个WPF Button,单击该WPF Button 会显示主窗体的边界。

    public class MyForm2 : Form {
    
        public MyForm2() {
            this.Size = new Size(600,600);
            this.StartPosition = FormStartPosition.CenterScreen;
            Button btn = new Button { Text = "New Window", AutoSize = true, AutoSizeMode = AutoSizeMode.GrowAndShrink };
            btn.Click += btn_Click;
            Controls.Add(btn);
        }
    
        void btn_Click(object sender, EventArgs e) {
            var w = new System.Windows.Window();
            System.Windows.Controls.Button wpfButton = new System.Windows.Controls.Button { Content = "Button" };
            wpfButton.Click += wpfButton_Click;
    
            w.SourceInitialized += w_SourceInitialized;
            w.Width = 400.0; // number of actual pixels might be different
            w.Height = 400.0; // depending on DPI. My laptop is 120 dpi, so 400.0 -> 400 * 120 / 96 = 500 pixels.
            w.Title = "WPF Window";
            w.Content = wpfButton;
            w.ShowDialog();
        }
    
        void wpfButton_Click(object sender, System.Windows.RoutedEventArgs e) {
            var wpfButton = (System.Windows.Controls.Button) sender;
            var wpfWindow = (System.Windows.Window) wpfButton.Parent;
            var helper = new WindowInteropHelper(wpfWindow);
            int GWL_HWNDPARENT = -8;
            IntPtr hwndMainForm = GetWindowLong(helper.Handle, GWL_HWNDPARENT);
            RECT r = new RECT();
            GetWindowRect(hwndMainForm, out r);
            SimpleWindow sw = new SimpleWindow { Handle = helper.Handle };
            MessageBox.Show(sw, "x:" + r.Left + " y:" + r.Top + " w:" + (r.Right - r.Left) + " h:" + (r.Bottom - r.Top), "Main Form Bounds");
        }
    
        private class SimpleWindow : System.Windows.Forms.IWin32Window {
            public IntPtr Handle { get; set; }
        }
    
        void w_SourceInitialized(object sender, EventArgs e) {
            var w = (System.Windows.Window) sender;
            WindowInteropHelper helper = new WindowInteropHelper(w);
            //w.WindowStartupLocation = System.Windows.WindowStartupLocation.CenterOwner; does nothing
            int GWL_HWNDPARENT = -8;
            SetWindowLongInternal(helper.Handle, GWL_HWNDPARENT, this.Handle);
    
            Rectangle r = this.Bounds;
            RECT r2 = new RECT();
            GetWindowRect(helper.Handle, out r2);
            int w2 = r2.Right - r2.Left;
            int h2 = r2.Bottom - r2.Top;
    
            int x2 = r.X + (r.Width - w2) / 2;
            int y2 = r.Y + (r.Height - h2) / 2;
    
            uint SWP_NOSIZE        = 0x0001;
            uint SWP_NOZORDER      = 0x0004;
            uint SWP_NOREDRAW      = 0x0008;
            uint SWP_NOACTIVATE    = 0x0010;
            uint SWP_NOCOPYBITS    = 0x0100;
            uint SWP_NOOWNERZORDER = 0x0200;
            uint flags = SWP_NOACTIVATE | SWP_NOCOPYBITS | SWP_NOOWNERZORDER | SWP_NOREDRAW | SWP_NOSIZE | SWP_NOZORDER;
    
            SetWindowPos(helper.Handle, IntPtr.Zero, x2, y2, 0, 0, flags);
        }
    
        [StructLayout(LayoutKind.Sequential)]
        private struct RECT {
            public int Left;
            public int Top;
            public int Right;
            public int Bottom;
        }
    
        [DllImport("user32.dll")]
        private static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);
    
        [DllImport("user32.dll", SetLastError=true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int w, int h, uint uFlags);
    
        [DllImport("user32.dll", SetLastError=true)]
        private static extern int SetWindowLong(IntPtr hWnd, int nIndex, IntPtr dwNewLong);
        [DllImport("user32.dll", SetLastError=true)]
        private static extern int SetWindowLongPtr(IntPtr hWnd, int nIndex, IntPtr dwNewLong);
    
        private static int SetWindowLongInternal(IntPtr hWnd, int nIndex, IntPtr dwNewLong) {
            if (IntPtr.Size == 4)
                return SetWindowLong(hWnd, nIndex, dwNewLong);
    
            return SetWindowLongPtr(hWnd, nIndex, dwNewLong);
        }
    
        [DllImport("user32.dll", SetLastError=true)]
        private static extern IntPtr GetWindowLongPtr(IntPtr hWnd, int nIndex);
        [DllImport("user32.dll", SetLastError=true)]
        private static extern IntPtr GetWindowLong(IntPtr hWnd, int nIndex);
    
        private static IntPtr GetWindowLongInternal(IntPtr hWnd, int nIndex) {
            if (IntPtr.Size == 4)
                return GetWindowLong(hWnd, (int) nIndex);
    
            return GetWindowLongPtr(hWnd, (int) nIndex);
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-11-15
      • 1970-01-01
      • 1970-01-01
      • 2011-09-26
      • 1970-01-01
      • 1970-01-01
      • 2013-01-17
      相关资源
      最近更新 更多