C # WPF 및 WinForms Interop-CenterOwner가 효과적이지 않음
UI에 WPF 및 WinForms를 사용하는 레거시 응용 프로그램에서 작업 중입니다. 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 부모를 중심으로 완벽하게 중앙에 배치됩니다 .
내 관점에서 이것은 SubWindow 의 ElementHost 부모와 관련이 있습니다.
누군가가 수동으로 위치를 계산하지 않고 부모 주위에 SubWindow 중심을 얻는 방법에 대해 조언 할 수 있습니까? (이와 유사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
안부 인사 Norbert
답변
사용 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);
}
}
이 버전은 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);
}
}