2010-10-06 9 views
5

W mojej aplikacji WPF hostuję zawartość Win32 przy użyciu HwndHost. Jednak tworzenie obiektu HwndHost nie tworzy okna macierzystego. Robi się to raczej w nadpisanej metodzie, którą później nazywa WPF.Wymuszenie zainicjowania pliku HwndHost

Moja hostowana zawartość wymaga uchwytu okna macierzystego okna do własnej inicjalizacji. Niestety, nie ma możliwości, aby wymusić utworzenie okna (tj. O wywołaniu WPF BuildWindowCore), więc mam drugi wątek, który odpytuje HwndHost, dopóki nie zostanie zainicjowany.

W .NET 4.0/WPF 4.0 dodano nową metodę WindowInteropHelper.EnsureHandle(). Miałem nadzieję, że to rozwiąże sytuację, ale działa tylko dla Okna, a nie HwndHost (który nie pochodzi z Okna). Czy masz sugestię, co mogę zrobić zamiast tego?

EDIT:

zapomniałem dodać jeszcze kilka ograniczeń dla możliwego rozwiązania:

  1. The HwndHost jest umieszczony w kontroli, które, w zależności od ustawień użytkownika, mogą być dzieckiem wniosek Głównego lub może zostać umieszczone w nowym oknie (za pośrednictwem menedżera dokowania innej firmy). Oznacza to, że podczas tworzenia okna nie wiem na pewno, czym będzie okno główne (a zatem jego hWnd).
  2. Podczas gdy kod natywny wymaga hWnd podczas jego inicjalizacji, okno jest wyświetlane tylko wtedy, gdy użytkownik zażąda, aby zostało ono wyświetlone (tzn. Jest niewidoczne na początku). Jeśli to możliwe, należy unikać wyświetlania okna tylko w celu natychmiastowego ukrycia go.

Odpowiedz

3

Wygląda na to, że nie ma idealnego rozwiązania. Zmieniłem nieco podejście do czasu zadawania pytania:

W konstruktorze mojej klasy pochodnej HwndHost mam (możliwego) rodzica hWnd jako jeden z parametrów. Następnie utworzę macierzyste okno przy użyciu natywnej metody CreateWindow(), używając danego nadrzędnego hWnd. Przechowuję utworzony plik hWnd w oddzielnej usłudze, której używam wszędzie zamiast właściwości Handle HwndHost. W ten sposób nie muszę pokazywać okna (to rozwiązuje ograniczenie nr 2).

W zastąpionej metodzie BuildWindowCore(), porównuję dany nadrzędny hWnd z podanym w konstruktorze. Jeśli są różne, ponownie przejmuję okno hostowane przy użyciu natywnej metody SetParent() (rozwiązuje to ograniczenie nr 1). Zauważ, że to zależy od tego, czy nikt nie przechowywał rodzica hWnd!

W kodzie, odpowiednie części (sprawdza pominięta):

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

Mam podobną sytuację i rozwiązać go w następujący sposób:

1) Utwórz klasę pochodną HwndHost że trwa Rect jako argument konstruktora (później wykorzystywane w BuildWindowCore):

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

2) utworzyć okno WPF z dzieckiem elementu border:

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

3) Utwórz instancję HwndHost i dodać go do th e okno w module obsługi Window_Loaded:

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

4) Również w programie obsługi Window_Loaded, przekazać HWND do inicjowania swojej rodzimej klasy albo poprzez P/Invoke lub C++/CLI. Moja rodzima klasa jest skonfigurowana do używania tego HWND jako jej rodzica i tworzy swój własny HWND jako dziecko.

+0

Występują dwa problemy: 1) Nie znam rodzica hWnd, ponieważ kontrola jest później pozycjonowana przez menedżera dokowania innej firmy, a zapisane ustawienia użytkownika określają, czy jest on wyświetlany samodzielnie, czy jako ". dziecko "głównego okna. 2) Kontrola za pomocą HwndHost może nie być w ogóle pokazywana na początku (w zależności od zapisanych ustawień użytkownika), ale przy starcie starszy kod wymaga hWnd. –

+0

Powinieneś być w stanie podłączyć się do zdarzenia Loaded na twojej kontroli i wykonać tam całą inicjalizację: http://msdn.microsoft.com/en-us/library/system.windows.frameworkelement.loaded.aspx. Jeśli starsza wersja kodu wymaga hwnd, musisz po prostu wstrzymać się od zrobienia czegokolwiek z dotychczasowym kodem, dopóki hwnd nie będzie gotowy (co musiałem zrobić). –

+0

Cytat z linku: "Występuje, gdy element jest ułożony, renderowany i gotowy do interakcji." Jeśli nie pokazuję kontrolki, ładowanie się nie uruchomi. –

0

Nieco późno, ale czy próbowałeś zadzwonić pod numer UpdateLayout() w sprawie kontroli hostingu? To zadziałało dla mnie

+0

Niestety, działa to tylko wtedy, gdy kontrola hostingu jest widoczna. Właśnie taki problem rozwiązuje funkcja Zapewnienia Jakości(), ale nie istnieje dla HwndHosts. –

0

I dodaje zdarzenie OnHandleCreated do mojego HwndHost -inherited klasy, która zawiera IntPtr uchwytu. To wydarzenie jest wywoływane wewnątrz BuildWindowCore().

Więc to sprowadza się do:

public class Win32WindowHost : HwndHost { ... }

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

działa wspaniale.

+0

Niestety, to nie działa w moim przypadku. BuildWindowCore() jest wywoływana tylko wtedy, gdy pokazany jest HwndHost. Nie ma innego sposobu na wymuszenie utworzenia natywnego pliku hwnd (to znaczy wymuszenie na WPF wywołania BuildWindowCore()). Jest to możliwe tylko dla czegoś wyprowadzonego z Window, poprzez WindowInteropHelper.EnsureHandle(). –