Interopérabilité C # WPF et WinForms - CenterOwner non efficace
Je travaille sur une application héritée qui utilise WPF et WinForms pour son interface utilisateur. WPF compense la grande majorité, mais la boîte de dialogue principale de l'application est toujours dans WinForms.
Jusqu'à présent, j'ai pu les faire travailler ensemble (grâce à System.Windows.Forms.Integration.ElementHost) mais je ne parviens pas à centrer les fenêtres WPF sur leur parent WinForms.
Mon code ressemble à ce qui suit.
Contrôle WPF (hébergé dans ElementHost)
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
var dialog = new SubWindow();
WindowOwnershipHelper.SetOwner(dialog, this);
dialog.ShowDialog();
}
OwnershipHelper (extrait dehttps://stackoverflow.com/a/36606974/13567181)
Cette classe établit la relation 'parent-enfant' afin que les boîtes de dialogue imbriquées s'ouvrent sur le même écran et se réduisent avec leur parent ...
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;
}
}
Le problème auquel je suis confronté est que lorsque dialog.ShowDialog () s'exécute, la fenêtre nouvellement ouverte n'est pas du tout centrée sur son propriétaire. Il est quelque part sur l'écran mais je ne comprends pas très bien comment il détermine son emplacement.
Fait intéressant, si je répète à nouveau le code ButtonBase_OnClick dans la classe SubWindow , cette nouvelle fenêtre est parfaitement centrée autour de son parent SubWindow .
De mon point de vue, cela a quelque chose à voir avec le ElementHost parent de subwindow .
Quelqu'un peut-il me conseiller sur la façon d'obtenir le centre SubWindow autour de son parent sans calculer manuellement sa position? (similaire à cecihttps://stackoverflow.com/a/42401001/13567181)
EDIT: Je viens de trouver cela sur MSDN - c'est en quelque sorte similaire mais je suis incertain. https://social.msdn.microsoft.com/Forums/vstudio/en-US/05768951-73cf-4daf-b369-0905ca7e5222/centering-wpf-window-on-winforms-owner-window?forum=wpf
Meilleures salutations Norbert
Réponses
Utilisez Application.Run(new MyForm2());
, puis cliquez sur le bouton pour créer un WPF Window
qui se concentre sur le formulaire principal.
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);
}
}
Cette version ajoute un WPF Button
qui, une fois cliqué, affiche les limites du formulaire principal.
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);
}
}