Проблема позиционирования окна при использовании SetParent()

Я пытаюсь установить childForm как дочерний элемент главного окна Excel с использованием API SetParent через PInvoke:

Form childForm = new MyForm();
IntPtr excelHandle = (IntPtr) excelApplication.Hwnd;
SetParent(childForm.Handle, excelHandle);
childForm.StartPosition = FormStartPosition.Manual;
childForm.Left = 0;
childForm.Top = 0;

Как вы можете видеть выше, мое намерение также состоит в том, чтобы расположить дочерний элемент в верхнем левом углу окна Excel. Однако по какой-то причине childForm всегда заканчивается в каком-то странном месте.

Что я делаю не так?

5 ответов

Хотя все ответы здесь предлагают совершенно логичные подходы, ни один из них не сработал для меня. Тогда я попробовал MoveWindow. По какой-то причине я не понимаю, это сработало.

Вот код:

[DllImport("user32.dll", SetLastError = true)]
internal static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint);

...

Form childForm = new MyForm();
IntPtr excelHandle = (IntPtr) excelApplication.Hwnd;
SetParent(childForm.Handle, excelHandle);
MoveWindow(childForm.Handle, 0, 0, childForm.Width, childForm.Height, true);

Когда используешь SetParent в форме, которая в настоящее время является дочерней по отношению к рабочему столу (другими словами, форма без родительского набора), вы должны установить WS_CHILD стиль и удалить WS_POPUP стиль. (См. Раздел "Примечания" в записи MSDN.) Windows требует, чтобы все собственные окна имели WS_CHILD набор стилей. Это также может быть причиной того, что свойства left и top сообщают / устанавливают неверные значения, потому что форма не знает, кто ее папа. Вы можете исправить это, позвонив SetWindowLong после SetParent, но прежде чем пытаться установить местоположение:

//Remove WS_POPUP style and add WS_CHILD style
const UInt32 WS_POPUP = 0x80000000;
const UInt32 WS_CHILD = 0x40000000;
int style = GetWindowLong(this.Handle, GWL_STYLE);
style = (style & ~(WS_POPUP)) | WS_CHILD;
SetWindowLong(this.Handle, GWL_STYLE, style);

Я полагаю, это зависит от вашего звонка в ShowDialog. Если вы вызываете ShowDialog без родительского параметра, родительский элемент сбрасывается.

Вы можете создать класс-оболочку, который реализует IWin32Window и возвращает HWND, чтобы преуспеть. Затем вы можете передать это в вызов ShowDialog childForm.

Вы также можете запросить позицию приложения Excel, используя GetWindowPos, а затем соответственно установить childForm.

Предполагая, что вы знаете, как получить hwnds окон, для которых вы хотите установить z-порядок, вы можете использовать этот pInvoke:

    public stati class WindowsApi 
    {
     [DllImport("user32.dll")]
    public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter,
        int X, int Y, int cx, int cy, uint uFlags);
    }



    public class WindowZOrderPositioner 
    {
         public void SetZOrder(IntPtr targetHwnd, IntPtr insertAfter)
         {
             IntPtr nextHwnd = IntPtr.Zero;

             WindowsAPI.SetWindowPos(targetHwnd, insertAfter, 0, 0, 0, 0, SetWindowPosFlags.NoMove | SetWindowPosFlags.NoSize | SetWindowPosFlags.NoActivate);
     }

Попробуйте несколько вещей, чтобы диагностировать проблему:

  • Поставьте точку останова после установки Left и Top, Left и Top читают ноль?
  • Вызовите SetParent последним.
  • Создайте метод, который снова устанавливает Left и Top и BeginInvoke метод.
  • Убедитесь, что ваше дочернее окно действительно является дочерним. Для этого вызовите ShowDialog и попробуйте нажать на родительское окно. Убедитесь, что окна мешают фокусироваться на родительском окне.
Другие вопросы по тегам