Проблема позиционирования окна при использовании 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 и попробуйте нажать на родительское окно. Убедитесь, что окна мешают фокусироваться на родительском окне.