Warm tip: This article is reproduced from serverfault.com, please click

C# WPF & WinForms Interop-CenterOwner无效

(C# WPF & WinForms Interop - CenterOwner not effective)

发布于 2020-11-27 23:33:26

我正在使用将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父级有关

有人可以建议我如何在不手动计算其位置的情况下如何使SubWindow围绕其父对象居中吗?(类似于此https://stackoverflow.com/a/42401001/13567181

编辑:我刚刚在MSDN上找到了它-某种程度上相似,但我不确定。https://social.msdn.microsoft.com/Forums/vstudio/zh-CN/05768951-73cf-4daf-b369-0905ca7e5222/centering-wpf-window-on-winforms-owner-window?forum=wpf

最好的问候诺伯特

Questioner
Norbert Hüthmayr
Viewed
11
Loathing 2020-11-28 09:58:18

使用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);
    }
}