2010-10-06 3 views
5

In meiner WPF-Anwendung hostet ich Win32-Inhalt mithilfe von HwndHost. Das Erstellen eines HwndHost erstellt jedoch nicht das systemeigene Fenster. Dies geschieht vielmehr in der überschriebenen Methode BuildWindowCore(), die irgendwann später von WPF aufgerufen wird.Erzwingen der Initialisierung eines HwndHost

Mein gehosteter Inhalt benötigt das Fensterhandle des nativen Fensters für seine eigene Initialisierung. Unglücklicherweise kann ich die Erstellung des Fensters nicht erzwingen (d. H., WPF ruft den BuildWindowCore auf), also habe ich einen zweiten Thread, der den HwndHost abfragt, bis er initialisiert wurde. In .NET 4.0/WPF 4.0 wurde eine neue Methode WindowInteropHelper.EnsureHandle() hinzugefügt. Ich hatte gehofft, dass dies die Situation lösen würde, aber es funktioniert nur für ein Fenster, nicht für einen HwndHost (der nicht von Window abgeleitet ist). Hast du einen Vorschlag, was ich stattdessen tun könnte?

EDIT:

Ich habe vergessen, einige weitere Einschränkungen für eine mögliche Lösung hinzuzufügen:

  1. Die HwndHost in einer Kontrolle gestellt wird, die je nach Benutzereinstellungen, Haupt ein Kind der Anwendung sein kann Fenster oder kann in einem neuen Fenster (über einen Docking-Manager eines Drittanbieters) platziert werden. Dies bedeutet, dass ich während der Erstellung des Fensters nicht sicher weiß, was das übergeordnete Fenster (und somit seine hWnd) sein wird.
  2. Während der native Code die hWnd während seiner Initialisierung benötigt, wird das Fenster nur angezeigt, wenn der Benutzer anfordert, es anzuzeigen (d. H. Es ist zunächst unsichtbar). Das Fenster zu zeigen, nur um es sofort wieder zu verstecken, sollte möglichst vermieden werden.

Antwort

3

Es scheint keine perfekte Lösung zu geben. Ich änderte meinen Ansatz leicht im Vergleich zum Zeitpunkt der gestellten Frage:

Im Konstruktor meiner von HwndHost abgeleiteten Klasse habe ich den (möglichen) Elternteil hWnd als einen der Parameter. Ich erstelle dann das native Fenster mit der nativen Methode CreateWindow() unter Verwendung des angegebenen übergeordneten hWnd. Ich speichere die erstellte hWnd in einer separaten Eigenschaft, die ich überall anstelle der Handle-Eigenschaft von HwndHost verwende. Auf diese Weise muss ich das Fenster nicht anzeigen (das löst Constraint # 2).

In der überschriebenen BuildWindowCore() Methode, ich vergleiche die angegebene Eltern hWnd mit der, die ich im Konstruktor gegeben wurde. Wenn sie unterschiedlich sind, repariere ich mein gehostetes Fenster mit der nativen Methode SetParent() (dies löst Constraint # 1). Beachten Sie, dass dies darauf angewiesen ist, dass niemand den übergeordneten hWnd speichert!

Im Code die entsprechenden Teile (Schecks weggelassen):

public class Win32Window : HwndHost 
{ 
    public Win32Window(IntPtr parentHwnd) 
    { 
     this.ParentHwnd = parentHwnd; 
     this.Win32Handle = NativeMethods.CreateWindowEx(/* parameters omitted*/); 
    } 

    public IntPtr Win32Handle { get; private set; } 
    private IntPtr ParentHwnd { get; set; } 

    protected override HandleRef BuildWindowCore(HandleRef hwndParent) 
    { 
     if (hwndParent.Handle != this.ParentHwnd) 
     { 
      NativeMethods.SetParent(this.Win32Handle, hwndParent.Handle); 
     } 

     return new HandleRef(this, this.Win32Handle); 
    } 
} 
1

Ich habe eine ähnliche Situation und ich löste es das wie folgt: Der

1) Erstellen Sie eine Klasse HwndHost abgeleitet, die eine Rect als Konstruktor-Argument (später in BuildWindowCore verwendet):

public class MyHwndHost : HwndHost 
{ 
    public MyHwndHost(Rect boundingBox) 
    { 
     BoundingBox = boundingBox; 
    } 
} 
erstellen

2) Element ein WPF-Fenster mit einem Kind Border:

<Window Loaded="Window_Loaded"> 
    <Border Name="HostElement" /> 
</Window> 

3) erstellen sie die HwndHost Instanz und fügen sie es th e-Fenster in dem Window_Loaded Handler:

void Window_Loaded(object sender, RoutedEventArgs e) 
{ 
    MyHwndHost host = new MyHwndHost(LayoutInformation.GetLayoutSlot(HostElement)); 
    HostElement.Child = host; 
} 

4) Auch in dem Window_Loaded Handler, die HWND auf die Initialisierung Ihrer Mutter Klasse übergeben, entweder durch P/Invoke oder C++/CLI. Ich habe meine native Klasse eingerichtet, um diese HWND als Eltern verwenden und es erstellt seine eigene HWND als Kind.

+0

Es gibt zwei Probleme: 1) Ich kenne den übergeordneten hWnd nicht, da das Steuerelement später von einem Docking-Manager eines Drittanbieters positioniert wird und die gespeicherten Benutzereinstellungen bestimmen, ob es alleine oder als " Kind "des Hauptfensters. 2) Die Steuerung mit dem HwndHost wird anfangs möglicherweise überhaupt nicht angezeigt (abhängig wiederum von den gespeicherten Benutzereinstellungen), aber beim Start benötigt der alte Code den hWnd. –

+0

Sie sollten in der Lage sein, sich in das Loaded-Ereignis auf Ihrem Steuerelement einzuloggen und dort alle Initialisierungen vorzunehmen: http://msdn.microsoft.com/en-us/library/system.windows.frameworkelement.loaded.aspx. Wenn der Legacy-Code das hwnd benötigt, dann müssen Sie nur etwas mit dem Legacy-Code machen, bis das hwnd fertig ist (was ich tun musste). –

+0

Zitat aus dem Link: "Tritt auf, wenn das Element ausgelegt, gerendert und bereit für die Interaktion ist." Wenn ich das Steuerelement nicht zeige, wird Loaded nicht ausgelöst. –

0

Ein bisschen spät, aber haben Sie versucht, UpdateLayout() auf dem Hosting-Steuerelement aufrufen? Das hat bei mir funktioniert

+0

Leider funktioniert das nur, wenn das Hosting-Steuerelement sichtbar ist. Das ist genau das Problem, das EnsureHandle() löst, aber für HwndHosts nicht existiert. –

0

Ich habe das Ereignis OnHandleCreated meiner HwndHost -inherited-Klasse, die den Griff IntPtr enthält. Dieses Ereignis wird innerhalb von BuildWindowCore() aufgerufen.

So kocht es auf:

public class Win32WindowHost : HwndHost { ... }

var host = new Win32WindowHost(); 
host.OnHandleCreated += (sender, e) => 
{ 
    var handle = e.Handle; 
    // Do stuff. 
}; 

Arbeitet ein Genuss.

+0

Leider funktioniert das in meinem Fall nicht. BuildWindowCore() wird nur aufgerufen, wenn der HwndHost angezeigt wird. Es gibt keine andere Möglichkeit, die Erstellung des nativen HWND zu erzwingen (d. H. WPF zum Aufruf von BuildWindowCore() zwingen). Dies ist nur für etwas möglich, das von Window über WindowInteropHelper.EnsureHandle() abgeleitet wurde. –