2010-10-06 2 views
5

В моем приложении WPF я размещаю содержимое Win32 с использованием HwndHost. Однако создание HwndHost не создает собственное окно. Скорее, это делается в переопределенном методе BuildWindowCore(), который позже называется WPF.Принудительная инициализация HwndHost

Мой размещенный контент требует дескриптора окна собственного окна для его собственной инициализации. К сожалению, я не могу заставить создать окно (т. Е. WPF вызывает BuildWindowCore), поэтому у меня есть второй поток, который опроса HwndHost до тех пор, пока он не будет инициализирован.

В .NET 4.0/WPF 4.0 был добавлен новый метод WindowInteropHelper.EnsureHandle(). Я надеялся, что это решит ситуацию, но она работает только для Window, а не HwndHost (которая не получается из Window). У вас есть предложение, что я мог бы сделать вместо этого?

EDIT:

Я забыл добавить еще некоторые ограничения для возможного решения:

  1. В HwndHost находится в элементе управления, который, в зависимости от настроек пользователя, может быть потомком приложения основной окно или может быть помещено в новое окно (через стороннего менеджера по стыковке). Это означает, что во время создания окна я точно не знаю, что такое родительское окно (и, следовательно, его hWnd).
  2. Хотя нативный код нуждается в hWnd во время его инициализации, окно отображается только тогда, когда пользователь запрашивает его показ (т. Е. Он невидим вначале). Необходимо показать окно, только чтобы скрыть его сразу же, следует избегать, если это возможно.

ответ

3

Кажется, что нет идеального решения. Я немного изменил свой подход по сравнению со временем заданного вопроса:

В конструкторе моего производного класса HwndHost у меня есть (возможно) родительский hWnd в качестве одного из параметров. Затем я создаю собственное окно, используя собственный метод CreateWindow(), используя данный родительский hWnd. Я храню созданный hWnd в отдельном свойстве, которое я использую везде, а не свойстве Handle HwndHost. Таким образом, мне не нужно показывать окно (это решает ограничение № 2).

В переопределенном методе BuildWindowCore() я сравниваю данный родительский hWnd с тем, который я дал в конструкторе. Если они отличаются друг от друга, я просматриваю мое размещенное окно с использованием собственного метода SetParent() (это решает ограничение №1). Обратите внимание, что это зависит от того, кто не хранит родительский hWnd!

В коде, соответствующие части (чеки опущенных):

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

У меня похожая ситуация, и я решил ее, выполнив следующие действия:

1) Создать производный класс HwndHost, который принимает Rect в качестве аргумента конструктора (впоследствии используется в BuildWindowCore):

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

2) Создайте окно WPF с пограничной дочерним элементом:

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

3) Создать экземпляр HwndHost и добавить его в го е окно в обработчике Window_Loaded:

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

4) Кроме того, в обработчике Window_Loaded, передать HWND к инициализации вашего родного класса либо через P/Invoke или C++/CLI. У меня есть мой собственный класс, настроенный для использования этого HWND в качестве его родителя, и он создает свой собственный HWND в качестве ребенка.

+0

Есть две проблемы: 1) Я не знаю родительского hWnd, так как элемент управления позже позиционируется сторонним менеджером стыковки, а сохраненные пользовательские настройки определяют, отображается ли это самостоятельно или как " ребенок "главного окна. 2) Управление с помощью HwndHost может вообще не отображаться (в зависимости от сохраненных настроек пользователя), но при запуске устаревший код нуждается в hWnd. –

+0

Вы должны иметь возможность подключиться к событию Loaded на вашем элементе управления и выполнить всю инициализацию там: http://msdn.microsoft.com/en-us/library/system.windows.frameworkelement.loaded.aspx. Если устаревший код нуждается в hwnd, вам просто нужно удержать что-либо с устаревшим кодом до тех пор, пока hwnd не будет готов (что я и должен был сделать). –

+0

Цитата из ссылки: «Происходит, когда элемент выложен, отображен и готов к взаимодействию». Если я не покажу контроль, Loaded не будет срабатывать. –

0

Немного поздно, но вы пробовали позвонить UpdateLayout() на управление хостингом? Это сработало для меня

+0

К сожалению, это работает только в том случае, если элемент управления хостингом виден. Это именно та проблема, которую решает EnsureHandle(), но она не существует для HwndHosts. –

0

я добавил событие OnHandleCreated к моему HwndHost -inherited класса, который содержит дескриптор IntPtr. Это событие вызывается внутри BuildWindowCore().

Так это сводится к:

public class Win32WindowHost : HwndHost { ... }

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

Работает лакомство.

+0

К сожалению, это не работает в моем случае. BuildWindowCore() вызывается только при отображении HwndHost. Нет другого способа заставить создание родного hwnd (т. Е. Заставить WPF вызывать BuildWindowCore()). Это возможно только для того, что происходит из окна, через WindowInteropHelper.EnsureHandle(). –

Смежные вопросы