¿Cómo puedo alojar contenido WPF en aplicaciones MFC?

Voy a responder mi propia pregunta aquí porque pasé unas horas reconstruyendo todo esto y quería compartir lo que encontré con la esperanza de salvar a alguien más de la excavación.

Hay un tutorial de MSDN que te lleva la mayor parte del camino hasta allí, pero hay un par de piezas clave que encontré en otros lugares. Por ejemplo, el tutorial le dice que coloque la línea [System :: STAThreadAttribute] antes de la definición _tWinMain (), pero si está implementando una aplicación MFC estándar, entonces no tiene _tWinMain () en su código fuente.

Si algo aquí no está claro, siéntase libre de hacer preguntas y editaré la respuesta para aclarar las cosas.

Paso 1: configure la aplicación MFC para comstackr con soporte CLR

La mejor forma de lograr la interoperabilidad entre C ++ nativo y el código .NET administrado es comstackr la aplicación como C ++ administrado en lugar de C ++ nativo. Esto se hace yendo a las propiedades de configuración del proyecto. En General hay una opción “Soporte de Common Language Runtime”. Establezca esto en “Common Language Runtime Support / clr”.

Paso 2: Agregue los ensamblados de WPF al proyecto

Haga clic derecho en el proyecto en el Explorador de soluciones y seleccione “Referencias”. Haga clic en “Agregar nueva referencia”. En la pestaña .NET, agregue WindowsBase, PresentationCore, PresentationFramework y System. Asegúrese de reconstruir todo después de agregar referencias para que puedan ser recogidos.

Paso 3: establece STAThreadAttribute en la aplicación MFC

WPF requiere que STAThreadAttribute se establezca en el hilo de UI principal. Establezca esto yendo a Propiedades de configuración del proyecto. En Linker-> Advanced hay una opción llamada “CLR Thread Attribute”. Establezca esto como “atributo de subprocesamiento STA”.

Paso 4: cree una instancia de HwndSource para envolver el componente de WPF

Sistema :: Windows :: Interop :: HwndSource es una clase .NET que maneja la interacción entre los componentes MFC y .NET. Crea uno con la siguiente syntax:

System::Windows::Interop::HwndSourceParameters^ sourceParams = gcnew System::Windows::Interop::HwndSourceParameters("MyWindowName"); sourceParams->PositionX = x; sourceParams->PositionY = y; sourceParams->ParentWindow = System::IntPtr(hWndParent); sourceParams->WindowStyle = WS_VISIBLE | WS_CHILD; System::Windows::Interop::HwndSource^ source = gcnew System::Windows::Interop::HwndSource(*sourceParams); source->SizeToContent = System::Windows::SizeToContent::WidthAndHeight; 

Agregue una variable de miembro HWND a la clase de diálogo y luego asígnela de la siguiente manera: m_hWnd = (HWND) source-> Handle.ToPointer ();

El objeto de origen y el contenido de WPF asociado permanecerán en existencia hasta que llame a :: DestroyWindow (m_hWnd).

Paso 5: agregue el control WPF al contenedor HwndSource

 System::Windows::Controls::WebBrowser^ browser = gcnew System::Windows::Controls::WebBrowser(); browser->Height = height; browser->Width = width; source->RootVisual = browser; 

Paso 6: mantener una referencia al objeto WPF

Dado que la variable del navegador se saldrá del scope después de que salgamos de la función que realiza la creación, tenemos que mantener de alguna manera una referencia a ella. Los objetos administrados no pueden ser miembros de objetos no administrados, pero puede usar una plantilla de envoltura llamada gcroot para realizar el trabajo.

Agregue una variable miembro a la clase de diálogo:

 #include  gcroot m_webBrowser; 

A continuación, agregue la siguiente línea al código en el Paso 5:

 m_webBrowser = browser; 

Ahora podemos acceder a propiedades y métodos en el componente WPF a través de m_webBrowser.