Принудительная инициализация HwndHost - PullRequest
5 голосов
/ 06 октября 2010

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

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

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

EDIT:

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

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

Ответы [ 4 ]

3 голосов
/ 24 июня 2011

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

В конструкторе моего класса, производного от 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 голос
/ 27 мая 2011

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

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

0 голосов
/ 19 февраля 2014

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

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

public class Win32WindowHost : HwndHost { ... }

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

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

0 голосов
/ 21 июня 2011

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

...