Принудительная инициализация HwndHost

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

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

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

РЕДАКТИРОВАТЬ:

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

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

4 ответа

Решение

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

В конструкторе моего производного от HwndHost класса у меня есть (возможный) родительский hWnd в качестве одного из параметров. Затем я создаю родное окно, используя родное CreateWindow() метод, используя данный родительский hWnd. Я храню созданный hWnd в отдельном свойстве, которое я использую везде вместо свойства HwndHost's Handle. Таким образом, мне не нужно показывать окно (это решает ограничение № 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) Создайте производный класс HwndHost, который принимает Rect в качестве аргумента конструктора (позже используется в BuildWindowCore):

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

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

<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 в качестве дочернего.

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

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

public class Win32WindowHost : HwndHost { ... }

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

Работает угощение.

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

Другие вопросы по тегам