2010-11-09 2 views
8

У меня есть приложение WPF, которое я хочу, чтобы оно размещалось внутри другого приложения, отличного от WPF. В реальной жизни это приложение, отличное от WPF, является ActiveX внутри Internet Explorer, но для иллюстрации проблемы я использую простое приложение Windows Forms.Хостинг WPF-приложения внутри приложения, отличного от WPF, с использованием WIn32 SetParent()

Я использую функцию Windows API SetParent, на которой уже есть десятки потоков. Тем не менее, я не могу найти что-либо написанное по моей точной проблеме: Небольшая область справа и внизу приложения WPF не раскрашена внутри окна приложения не WPF.

скользящего окна WPF сам по себе:
alt text

окна WPF с окном Winform приложения в качестве родителя:
alt text

не испытывает проблемы, если своп приложения WPF с WinForms или простое приложение Win32 (например, Блокнот).

Код WinForm выглядит следующим образом:

private void Form1_Load(object sender, EventArgs e) 
    { 
     // Start process 
     var psi = new ProcessStartInfo("c:\\wpfapp\\wpfapp\\bin\\Debug\\wpfapp.exe"); 
     psi.WindowStyle = ProcessWindowStyle.Normal; 
     _process = Process.Start(psi); 

     // Sleep until new process is ready 
     Thread.Sleep(1000); 

     // Set new process's parent to this window 
     SetParent(_process.MainWindowHandle, this.Handle); 

     // Remove WS_POPUP and add WS_CHILD window style to child window 
     const int GWL_STYLE = -16; 
     const long WS_POPUP = 0x80000000; 
     const long WS_CHILD = 0x40000000; 
     long style = GetWindowLong(_process.MainWindowHandle, GWL_STYLE); 
     style = (style & ~(WS_POPUP)) | WS_CHILD; 
     SetWindowLong(_process.MainWindowHandle, GWL_STYLE, style); 

     // Move and resize child window to fit into parent's 
     MoveWindow(_process.MainWindowHandle, 0, 0, this.Width, this.Height, true); 
    } 

Примечание: Я знаю, что такое использование SetParent не обязательно рекомендуемая практика, но я хочу, и нужно выяснить, как это сделать это путь, поэтому, пожалуйста, дайте мне :)

+0

Я должен добавить, что, как мне кажется, MoveWindow вызывает «обрезку». До этого все в порядке, но WPF, очевидно, имеет неправильное положение и размер. – jonsb

+0

Не было бы проще использовать Silverlight? – TeaDrivenDev

+0

Я не вижу, где вы устанавливаете размер родительского окна, и это явно слишком мало. Вам нужно изменить размер родительского окна, чтобы после вычитания неклиентской области клиентская область была размером, необходимым для вашего приложения WPF. –

ответ

6

Я нашел чюо rkaround, который работает достаточно хорошо: Call MoveWindow перед тем SetParent:

private void Form1_Load(object sender, EventArgs e) 
{ 
    // Start process    
    var psi = new ProcessStartInfo("C:\\WpfApp\\WpfApp.exe"); 
    psi.WindowStyle = ProcessWindowStyle.Normal; 
    _process = Process.Start(psi); 

    // Sleep until new process is ready 
    Thread.Sleep(3000); 

    // Move and resize child window to fit into parent's 
    Rectangle rect; 
    GetClientRect(this.Handle, out rect); 
    MoveWindow(_process.MainWindowHandle, 0, 0, rect.Right - rect.Left, rect.Bottom - rect.Top, true); 

    // Set new process's parent to this window 
    SetParent(_process.MainWindowHandle, this.Handle); 

    // Remove WS_POPUP and add WS_CHILD window style to child window 
    long style = GetWindowLong(_process.MainWindowHandle, GWL_STYLE); 
    style = (style & ~(WS_POPUP) & ~(WS_CAPTION)) & ~(WS_THICKFRAME) | WS_CHILD; 
    SetWindowLong(_process.MainWindowHandle, GWL_STYLE, style); 
}   

В обработчик событий SizeChanged, я беру ребенок из родителей, вызовите MoveWindow и переместить его обратно в родитель. Для того, чтобы уменьшить мерцание, я скрыть окно при выполнении этих операций:

private void Form1_SizeChanged(object sender, EventArgs e) 
{ 
    ShowWindow(_process.MainWindowHandle, SW_HIDE); 

    var ptr = new IntPtr(); 
    SetParent(_process.MainWindowHandle, ptr); 

    Rectangle rect; 
    GetClientRect(this.Handle, out rect); 

    MoveWindow(_process.MainWindowHandle, 0, 0, rect.Right - rect.Left, rect.Bottom - rect.Top, true);    
    SetParent(_process.MainWindowHandle, this.Handle); 
    ShowWindow(_process.MainWindowHandle, SW_SHOW); 
} 

это не объясняет, почему MoveWindow не работает после SetParent, но это решает мою проблему, так что я буду отмечать это как ответ, если что- лучше подходит.

+0

Лучше использовать execProcess.WaitForInputIdle() вместо Thread.Sleep (3000) для ожидания до тех пор, пока процесс не будет запущен и не будет работать ... – Spawnrider

3

Вы, вероятно, следует изменить размер окна WPF, так что она вписывается в client area своего нового родительского окна:

[StructLayout(LayoutKind.Sequential)] 
public struct RECT 
{ 
    public int Left; 
    public int Top; 
    public int Right; 
    public int Bottom; 
} 

[DllImport("user32.dll")] 
static extern bool GetClientRect(IntPtr hWnd, out RECT lpRect); 

// Move and resize child window to fit into parent's client area. 
RECT rc = new RECT(); 
GetClientRect(this.Handle, out rc); 
MoveWindow(_process.MainWindowHandle, 0, 0, rc.Right - rc.Left, 
    rc.Bottom - rc.Top, true) 
+0

Спасибо, вы правы в области клиентов. Я сделал это, как указано выше, и меньшая сумма теперь окрашена вне родительского окна, чем раньше. Также кажется, что количество, нарисованное снаружи справа, равно ширине левой и правой родительских границ, а сумма внизу равна верхней и нижней родительской строке заголовка и границам. Если я установил границу родителя на «none», это будет правильно. Что с этим? – jonsb

+0

@ jonsb, странно, клиентская область не должна принимать во внимание декорации окна. Можете ли вы [p/invoke GetWindowRect()] (http://www.pinvoke.net/default.aspx/user32.getwindowrect) на 'this.Handle' и подтвердить, что ширина и высота результата такие же, как' GetClientRect() '? –

+0

Да, GetClientRect не учитывает декорации окон, и окно действительно изменяет размер до нужного размера. GetWindowRect() возвращает другую (большую) ширину и высоту. Однако содержимое окна WPF выходит за пределы собственного окна. – jonsb

0

Я использовал форму/UserControl с панелью. Затем я использовал Panel в качестве нового родителя и установил новое положение и размер панели с помощью SetWindowPos в дочернее окно.

ПРИМЕЧАНИЕ. MS рекомендует использовать SetWindowPos после использования SetWindowLong (или SetWindowLongPtr) для активации новых стилей.