Tutorial: Hospedar un control de Win32 en WPF

Windows Presentation Foundation (WPF) proporciona un entorno enriquecido para crear aplicaciones. Sin embargo, cuando se tiene una inversión sustancial en código Win32, puede resultar más eficaz reutilizar al menos parte de ese código en la aplicación WPF en lugar de volver a escribirlo todo por completo. WPF proporciona un mecanismo sencillo para hospedar una ventana Win32 en una página WPF.

En este tema se explica cómo crear una aplicación, Hosting a Win32 ListBox Control in WPF Sample, que hospeda un control de cuadro de lista de Win32. Este procedimiento general se puede extender para hospedar cualquier ventana Win32.

Este tema contiene las secciones siguientes.

  • Requisitos
  • El procedimiento básico
  • Implementar el diseño de página
  • Implementar una clase para hospedar el control Microsoft Win32
  • Hospedar el control en la página
  • Implementar la comunicación entre el control y la página
  • Temas relacionados

Requisitos

En este tema se da por hecho que está familiarizado con la programación básica en WPF y Win32. Para obtener una introducción básica a la programación en WPF, vea Introducción (WPF). Para obtener una introducción a la programación en Win32, puede consultar cualquiera de los numerosos libros sobre el tema, en particular Programming Windows escrito por Charles Petzold.

Como el ejemplo que acompaña a este tema se implementa en C#, se utiliza Platform Invocation Services (PInvoke) para tener acceso a la API de Win32. Puede ser útil tener algún conocimiento sobre PInvoke, pero no esencial.

NotaNota

En este tema se incluyen varios ejemplos de código del ejemplo asociado.Sin embargo, para facilitar la legibilidad, no se incluye el código de ejemplo completo.Puede obtener o ver el código completo en Hosting a Win32 ListBox Control in WPF Sample.

El procedimiento básico

En esta sección se describe el procedimiento básico para hospedar una ventana Win32 en una página de WPF. En las demás secciones se explican los detalles de cada paso.

El procedimiento de hospedaje básico es el siguiente:

  1. Implemente una página de WPF para hospedar la ventana. Una técnica es crear un elemento Border para reservar una sección de la página de la ventana hospedada.

  2. Implemente una clase para hospedar el control que se hereda de HwndHost.

  3. En esta clase, invalide el miembro BuildWindowCore de la clase HwndHost.

  4. Cree la ventana hospedada como un elemento secundario de la ventana que contiene la página de WPF. Aunque la programación de WPF convencional no necesita utilizar esta ventana explícitamente, la página de hospedaje es una ventana con un controlador (HWND). Recibirá la página HWND a través del parámetro hwndParent del método BuildWindowCore. La ventana hospedada se debe crear como un elemento secundario de HWND.

  5. Una vez creada la ventana host, devuelva el HWND de la ventana hospedada. Si desea hospedar uno o varios controles Win32, normalmente creará una ventana host como un elemento secundario de HWND y convertirá los controles en elementos secundarios de esa ventana host. Alojar los controles en una ventana host es un modo simple de permitir que la página WPF reciba las notificaciones de los controles y de abordar algunos de los problemas de Win32 con las notificaciones a través de los límites de HWND.

  6. Controle algunos de los mensajes enviados a la ventana host, como las notificaciones de los controles secundarios. Existen dos formas de lograr esto.

    • Si prefiere controlar los mensajes en la clase de hospedaje, invalide el método WndProc de la clase HwndHost.

    • Si prefiere que WPF controle los mensajes, controle el evento MessageHook de la clase HwndHost en el código subyacente. Este evento se produce para cada mensaje recibido por la ventana hospedada. Si elige esta opción, debe invalidar igualmente WndProc, pero sólo necesita una implementación mínima.

  7. Invalide los métodos DestroyWindowCore y WndProc de HwndHost. Debe invalidar estos métodos para cumplir el contrato de HwndHost, pero es posible que sólo necesite proporcionar una implementación mínima.

  8. En su archivo de código subyacente, cree una instancia de la clase que hospeda los controles y conviértala en un elemento secundario del elemento Border que va a hospedar la ventana.

  9. Comuníquese con la ventana hospedada enviándole mensajes Microsoft Windows y controlando los mensajes de sus ventanas secundarias, como las notificaciones enviadas por los controles.

Implementar el diseño de página

El diseño de la página WPF que hospeda el control ListBox consta de dos regiones. El margen izquierdo de la página hospeda varios controles WPF que proporcionan una user interface (UI) que le permite manipular el control Win32. La esquina superior derecha de la página tiene una región cuadrada para el control ListBox hospedado.

El código para implementar este diseño es bastante simple. El elemento raíz es un DockPanel que tiene dos elementos secundarios. El primero es un elemento Border que hospeda el control ListBox. Ocupa un espacio cuadrado de 200 x 200 en la esquina superior derecha de la página. El segundo es un elemento StackPanel que contiene un conjunto de los controles WPF que muestran información y le permiten manipular el control ListBox estableciendo las propiedades de interoperación expuestas. Para cada uno de los elementos que son elementos secundarios de StackPanel, consulte el material de referencia de los distintos elementos utilizados para obtener detalles sobre qué son estos elemento y qué hacen. Estos elementos se muestran en el código de ejemplo siguiente, pero no se ofrece una explicación de ellos (no son necesarios para el modelo básico de interoperación; se proporcionan para agregar interactividad al ejemplo).

<Window
  xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
  x:Class="WPF_Hosting_Win32_Control.HostWindow"
  Name="mainWindow"
  Loaded="On_UIReady">

  <DockPanel Background="LightGreen">
    <Border Name="ControlHostElement"
    Width="200"
    Height="200"
    HorizontalAlignment="Right"
    VerticalAlignment="Top"
    BorderBrush="LightGray"
    BorderThickness="3"
    DockPanel.Dock="Right"/>
    <StackPanel>
      <Label HorizontalAlignment="Center"
        Margin="0,10,0,0"
        FontSize="14"
        FontWeight="Bold">Control the Control</Label>
      <TextBlock Margin="10,10,10,10" >Selected Text: <TextBlock  Name="selectedText"/></TextBlock>
      <TextBlock Margin="10,10,10,10" >Number of Items: <TextBlock  Name="numItems"/></TextBlock>

      <Line X1="0" X2="200"
        Stroke="LightYellow"
        StrokeThickness="2"
        HorizontalAlignment="Center"
        Margin="0,20,0,0"/>

      <Label HorizontalAlignment="Center"
        Margin="10,10,10,10">Append an Item to the List</Label>
      <StackPanel Orientation="Horizontal">
        <Label HorizontalAlignment="Left"
          Margin="10,10,10,10">Item Text</Label>
        <TextBox HorizontalAlignment="Left"
          Name="txtAppend"
          Width="200"
          Margin="10,10,10,10"></TextBox>
      </StackPanel>

      <Button HorizontalAlignment="Left"
        Click="AppendText"
        Width="75"
        Margin="10,10,10,10">Append</Button>

      <Line X1="0" X2="200"
        Stroke="LightYellow"
        StrokeThickness="2"
        HorizontalAlignment="Center"
        Margin="0,20,0,0"/>

      <Label HorizontalAlignment="Center"
        Margin="10,10,10,10">Delete the Selected Item</Label>

      <Button Click="DeleteText"
        Width="125"
        Margin="10,10,10,10"
        HorizontalAlignment="Left">Delete</Button>
    </StackPanel>
  </DockPanel>
</Window>  

Implementar una clase para hospedar el control Microsoft Win32

El núcleo de este ejemplo es la clase que realmente hospeda el control, ControlHost.cs. Se hereda de HwndHost. El constructor toma dos parámetros, el alto y el ancho, que se corresponden con el alto y ancho del elemento Border que hospeda el control ListBox. Estos valores se utilizan después para garantizar que el tamaño del control coincide con el elemento Border.

  Public Class ControlHost
      Inherits HwndHost
    Private hwndControl As IntPtr
    Private hwndHost As IntPtr
    Private hostHeight, hostWidth As Integer

    Public Sub New(ByVal height As Double, ByVal width As Double)
            hostHeight = CInt(height)
            hostWidth = CInt(width)
    End Sub
public class ControlHost : HwndHost
{
  IntPtr hwndControl;
  IntPtr hwndHost;
  int hostHeight, hostWidth;

  public ControlHost(double height, double width)
  {
    hostHeight = (int)height;
    hostWidth = (int)width;
  }

También hay un conjunto de constantes. Estas constantes se obtienen principalmente de Winuser.h y permiten utilizar nombres convencionales al llamar a las funciones Win32.

    Friend Const WS_CHILD As Integer = &H40000000, WS_VISIBLE As Integer = &H10000000, LBS_NOTIFY As Integer = &H00000001, HOST_ID As Integer = &H00000002, LISTBOX_ID As Integer = &H00000001, WS_VSCROLL As Integer = &H00200000, WS_BORDER As Integer = &H00800000
internal const int
  WS_CHILD = 0x40000000,
  WS_VISIBLE = 0x10000000,
  LBS_NOTIFY = 0x00000001,
  HOST_ID = 0x00000002,
  LISTBOX_ID = 0x00000001,
  WS_VSCROLL = 0x00200000,
  WS_BORDER = 0x00800000;

Invalidar BuildWindowCore para crear la ventana Microsoft Win32

Este método se invalida para crear la ventana Win32 que se hospedará en la página y realizar la conexión entre la ventana y la página. Como este ejemplo implica hospedar un control ListBox Control, se crean dos ventanas. La primera es la ventana que se hospeda realmente en la página WPF. El control ListBox se crea como un elemento secundario de esa ventana.

La razón de este enfoque es simplificar el proceso de recepción de notificaciones desde el control. La clase HwndHost permite procesar los mensajes enviados a la ventana hospedada. Si hospeda directamente un control Win32, recibirá los mensajes enviados al bucle de mensajes interno del control. Puede mostrar el control y enviarle mensajes, pero no recibirá las notificaciones que el control envía a su ventana primaria. Esto significa, entre otras cosas, que no hay forma de detectar cuándo el usuario interactúa con el control. En lugar de ello, cree una ventana host y convierta el control en un elemento secundario de esa ventana. Esto le permite procesar los mensajes de la ventana host incluidas las notificaciones enviadas a ella por el control. Por comodidad, como la ventana host es básicamente un contenedor simple del control, el paquete recibirá el nombre de control ListBox.

Crear la ventana host y el control ListBox

Puede utilizar PInvoke para crear una ventana host para el control creando y registrando una clase de ventana, etc. Sin embargo, un procedimiento mucho más simple es crear una ventana con la clase de ventana "estática" predefinida. De esta forma, obtendrá el procedimiento de la ventana que necesita para recibir las notificaciones del control, sin tener apenas que escribir código.

El HWND del control se expone a través de una propiedad de sólo lectura, que la página host puede utilizar para enviar mensajes al control.

    Public ReadOnly Property hwndListBox() As IntPtr
      Get
          Return hwndControl
      End Get
    End Property
public IntPtr hwndListBox
{
  get { return hwndControl; }
}

El control ListBox se crea como un elemento secundario de la ventana host. El alto y ancho de ambas ventanas se establecen en los valores pasados al constructor, descritos anteriormente. Esto garantiza que el tamaño de la ventana host y el control sea idéntico al del área reservada en la página. Una vez creadas las ventanas, el ejemplo devuelve un objeto HandleRef que contiene el HWND de la ventana host.

    Protected Overrides Function BuildWindowCore(ByVal hwndParent As HandleRef) As HandleRef
      hwndControl = IntPtr.Zero
      hwndHost = IntPtr.Zero

      hwndHost = CreateWindowEx(0, "static", "", WS_CHILD Or WS_VISIBLE, 0, 0, hostWidth, hostHeight, hwndParent.Handle, New IntPtr(HOST_ID), IntPtr.Zero, 0)

      hwndControl = CreateWindowEx(0, "listbox", "", WS_CHILD Or WS_VISIBLE Or LBS_NOTIFY Or WS_VSCROLL Or WS_BORDER, 0, 0, hostWidth, hostHeight, hwndHost, New IntPtr(LISTBOX_ID), IntPtr.Zero, 0)

      Return New HandleRef(Me, hwndHost)
    End Function
protected override HandleRef BuildWindowCore(HandleRef hwndParent)
{
  hwndControl = IntPtr.Zero;
  hwndHost = IntPtr.Zero;

  hwndHost = CreateWindowEx(0, "static", "",
                            WS_CHILD | WS_VISIBLE,
                            0, 0,
                            hostWidth, hostHeight, 
                            hwndParent.Handle,
                            (IntPtr)HOST_ID,
                            IntPtr.Zero,
                            0);

  hwndControl = CreateWindowEx(0, "listbox", "",
                                WS_CHILD | WS_VISIBLE | LBS_NOTIFY
                                  | WS_VSCROLL | WS_BORDER,
                                0, 0,
                                hostWidth, hostHeight, 
                                hwndHost,
                                (IntPtr) LISTBOX_ID,
                                IntPtr.Zero,
                                0);

  return new HandleRef(this, hwndHost);
}
    'PInvoke declarations
    <DllImport("user32.dll", EntryPoint := "CreateWindowEx", CharSet := CharSet.Unicode)>
    Friend Shared Function CreateWindowEx(ByVal dwExStyle As Integer, ByVal lpszClassName As String, ByVal lpszWindowName As String, ByVal style As Integer, ByVal x As Integer, ByVal y As Integer, ByVal width As Integer, ByVal height As Integer, ByVal hwndParent As IntPtr, ByVal hMenu As IntPtr, ByVal hInst As IntPtr, <MarshalAs(UnmanagedType.AsAny)> ByVal pvParam As Object) As IntPtr
    End Function
//PInvoke declarations
[DllImport("user32.dll", EntryPoint = "CreateWindowEx", CharSet = CharSet.Unicode)]
internal static extern IntPtr CreateWindowEx(int dwExStyle,
                                              string lpszClassName,
                                              string lpszWindowName,
                                              int style,
                                              int x, int y,
                                              int width, int height,
                                              IntPtr hwndParent,
                                              IntPtr hMenu,
                                              IntPtr hInst,
                                              [MarshalAs(UnmanagedType.AsAny)] object pvParam);

Implementar DestroyWindow y WndProc

Además de BuildWindowCore, debe invalidar también los métodos WndProc y DestroyWindowCore de HwndHost. En este ejemplo, los mensajes del control se controlan mediante el controlador MessageHook, por lo que la implementación de WndProc y DestroyWindowCore es mínima. En el caso de WndProc, establezca handled en false para indicar que el mensaje no se ha controlado y devuelva 0. Para DestroyWindowCore, simplemente destruya la ventana.

    Protected Overrides Function WndProc(ByVal hwnd As IntPtr, ByVal msg As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr, ByRef handled As Boolean) As IntPtr
      handled = False
      Return IntPtr.Zero
    End Function

    Protected Overrides Sub DestroyWindowCore(ByVal hwnd As HandleRef)
      DestroyWindow(hwnd.Handle)
    End Sub
protected override IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
  handled = false;
  return IntPtr.Zero;
}

protected override void DestroyWindowCore(HandleRef hwnd)
{
  DestroyWindow(hwnd.Handle);
}
    <DllImport("user32.dll", EntryPoint := "DestroyWindow", CharSet := CharSet.Unicode)>
    Friend Shared Function DestroyWindow(ByVal hwnd As IntPtr) As Boolean
    End Function
[DllImport("user32.dll", EntryPoint = "DestroyWindow", CharSet = CharSet.Unicode)]
internal static extern bool DestroyWindow(IntPtr hwnd);

Hospedar el control en la página

Para hospedar el control en la página, primero debe crear una nueva instancia de la clase ControlHost. Pase el alto y ancho del elemento de esquina que contiene el control (ControlHostElement) al constructor ControlHost. De esta forma, se asegurará de que el control ListBox tiene el tamaño correcto. A continuación, hospede el control en la página asignando el objeto ControlHost a la propiedad Child del Border host.

En el ejemplo se asocia un controlador al evento MessageHook de ControlHost para recibir los mensajes del control. Este evento se provoca para cada mensaje enviado a la ventana hospedada. En este caso, éstos son los mensajes enviados a la ventana que contiene el control ListBox real, incluidas las notificaciones del control. En el ejemplo se llama a SendMessage para obtener información del control y modificar su contenido. Los detalles sobre cómo la página se comunica con el control se describen en la sección siguiente.

NotaNota

Observe que hay dos declaraciones PInvoke para SendMessage.Esto es necesario porque una utiliza el parámetro wParam para pasar una cadena y la otra lo utiliza para pasar un valor entero.Necesita una declaración distinta para cada firma con el fin de garantizar que las referencias a los datos se calculan correctamente.

Partial Public Class HostWindow
    Inherits Window
    Private selectedItem As Integer
    Private hwndListBox As IntPtr
    Private listControl As ControlHost
    Private app As Application
    Private myWindow As Window
    Private itemCount As Integer

    Private Sub On_UIReady(ByVal sender As Object, ByVal e As EventArgs)
        app = System.Windows.Application.Current
        myWindow = app.MainWindow
        myWindow.SizeToContent = SizeToContent.WidthAndHeight
        listControl = New ControlHost(ControlHostElement.ActualHeight, ControlHostElement.ActualWidth)
        ControlHostElement.Child = listControl
        AddHandler listControl.MessageHook, AddressOf ControlMsgFilter
        hwndListBox = listControl.hwndListBox
        For i As Integer = 0 To 14 'populate listbox
            Dim itemText As String = "Item" & i.ToString()
            SendMessage(hwndListBox, LB_ADDSTRING, IntPtr.Zero, itemText)
        Next i
        itemCount = SendMessage(hwndListBox, LB_GETCOUNT, IntPtr.Zero, IntPtr.Zero)
        numItems.Text = "" & itemCount.ToString()
    End Sub
    public partial class HostWindow : Window
    {
    int selectedItem;
    IntPtr hwndListBox;
    ControlHost listControl;
    Application app;
    Window myWindow;
    int itemCount;

    private void On_UIReady(object sender, EventArgs e)
    {
      app = System.Windows.Application.Current;
      myWindow = app.MainWindow;
      myWindow.SizeToContent = SizeToContent.WidthAndHeight;
      listControl = new ControlHost(ControlHostElement.ActualHeight, ControlHostElement.ActualWidth);
      ControlHostElement.Child = listControl;
      listControl.MessageHook += new HwndSourceHook(ControlMsgFilter);
      hwndListBox = listControl.hwndListBox;
      for (int i = 0; i < 15; i++) //populate listbox
      {
        string itemText = "Item" + i.ToString();
        SendMessage(hwndListBox, LB_ADDSTRING, IntPtr.Zero, itemText);
      }
      itemCount = SendMessage(hwndListBox, LB_GETCOUNT, IntPtr.Zero, IntPtr.Zero);
      numItems.Text = "" +  itemCount.ToString();
    }

Private Function ControlMsgFilter(ByVal hwnd As IntPtr, ByVal msg As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr, ByRef handled As Boolean) As IntPtr
    Dim textLength As Integer

    handled = False
    If msg = WM_COMMAND Then
        Select Case CUInt(wParam.ToInt32()) >> 16 And &HFFFF 'extract the HIWORD
            Case LBN_SELCHANGE 'Get the item text and display it
                selectedItem = SendMessage(listControl.hwndListBox, LB_GETCURSEL, IntPtr.Zero, IntPtr.Zero)
                textLength = SendMessage(listControl.hwndListBox, LB_GETTEXTLEN, IntPtr.Zero, IntPtr.Zero)
                Dim itemText As New StringBuilder()
                SendMessage(hwndListBox, LB_GETTEXT, selectedItem, itemText)
                selectedText.Text = itemText.ToString()
                handled = True
        End Select
    End If
    Return IntPtr.Zero
End Function
Friend Const LBN_SELCHANGE As Integer = &H1, WM_COMMAND As Integer = &H111, LB_GETCURSEL As Integer = &H188, LB_GETTEXTLEN As Integer = &H18A, LB_ADDSTRING As Integer = &H180, LB_GETTEXT As Integer = &H189, LB_DELETESTRING As Integer = &H182, LB_GETCOUNT As Integer = &H18B

<DllImport("user32.dll", EntryPoint:="SendMessage", CharSet:=CharSet.Unicode)>
Friend Shared Function SendMessage(ByVal hwnd As IntPtr, ByVal msg As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As Integer
End Function

<DllImport("user32.dll", EntryPoint:="SendMessage", CharSet:=CharSet.Unicode)>
Friend Shared Function SendMessage(ByVal hwnd As IntPtr, ByVal msg As Integer, ByVal wParam As Integer, <MarshalAs(UnmanagedType.LPWStr)> ByVal lParam As StringBuilder) As Integer
End Function

<DllImport("user32.dll", EntryPoint:="SendMessage", CharSet:=CharSet.Unicode)>
Friend Shared Function SendMessage(ByVal hwnd As IntPtr, ByVal msg As Integer, ByVal wParam As IntPtr, ByVal lParam As String) As IntPtr
End Function

private IntPtr ControlMsgFilter(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
  int textLength;

  handled = false;
  if (msg == WM_COMMAND)
  {
    switch ((uint)wParam.ToInt32() >> 16 & 0xFFFF) //extract the HIWORD
    {
      case LBN_SELCHANGE : //Get the item text and display it
        selectedItem = SendMessage(listControl.hwndListBox, LB_GETCURSEL, IntPtr.Zero, IntPtr.Zero);
        textLength = SendMessage(listControl.hwndListBox, LB_GETTEXTLEN, IntPtr.Zero, IntPtr.Zero);
        StringBuilder itemText = new StringBuilder();
        SendMessage(hwndListBox, LB_GETTEXT, selectedItem, itemText);
        selectedText.Text = itemText.ToString();
        handled = true;
        break;
    }
  }
  return IntPtr.Zero;
}
internal const int
  LBN_SELCHANGE = 0x00000001,
  WM_COMMAND = 0x00000111,
  LB_GETCURSEL = 0x00000188,
  LB_GETTEXTLEN = 0x0000018A,
  LB_ADDSTRING = 0x00000180, 
  LB_GETTEXT = 0x00000189,
  LB_DELETESTRING = 0x00000182,
  LB_GETCOUNT = 0x0000018B;

[DllImport("user32.dll", EntryPoint = "SendMessage", CharSet = CharSet.Unicode)]
internal static extern int SendMessage(IntPtr hwnd,
                                       int msg,
                                       IntPtr wParam,
                                       IntPtr lParam);

[DllImport("user32.dll", EntryPoint = "SendMessage", CharSet = CharSet.Unicode)]
internal static extern int SendMessage(IntPtr hwnd,
                                       int msg,
                                       int wParam, 
                                       [MarshalAs(UnmanagedType.LPWStr)] StringBuilder lParam);

[DllImport("user32.dll", EntryPoint = "SendMessage", CharSet = CharSet.Unicode)]
internal static extern IntPtr SendMessage(IntPtr hwnd,
                                          int msg,
                                          IntPtr wParam,
                                          String lParam);

Implementar la comunicación entre el control y la página

El control se manipula enviándole mensajes Windows. El control le indica cuándo el usuario interactúa con él enviando notificaciones a su ventana host. En el ejemplo Hosting a Win32 ListBox Control in WPF Sample se incluye una interfaz de usuario que proporciona varios ejemplos sobre cómo funciona esto:

  • Asocie un elemento a la lista.

  • Elimine el elemento seleccionado de la lista

  • Muestre el texto del elemento actualmente seleccionado.

  • Muestre el número de elementos de la lista.

El usuario puede seleccionar también un elemento del cuadro de lista haciendo clic en él, como ocurre en cualquier aplicación Win32 convencional. Los datos mostrados se actualizan cada vez que el usuario cambia el estado del cuadro de lista seleccionándolo, agregando o anexando un elemento.

Para anexar elementos, envíe un mensaje LB_ADDSTRING al cuadro de lista. Para eliminar elementos, envíe LB_GETCURSEL para obtener el índice de la selección actual y, después, LB_DELETESTRING para eliminar el elemento. En el ejemplo se envía también LB_GETCOUNT y se utiliza el valor devuelto para actualizar la pantalla que muestra el número de elementos. Ambas instancias de SendMessage utilizan una de las declaraciones PInvoke descritas en la sección anterior.

Private Sub AppendText(ByVal sender As Object, ByVal args As EventArgs)
    If txtAppend.Text <> String.Empty Then
        SendMessage(hwndListBox, LB_ADDSTRING, IntPtr.Zero, txtAppend.Text)
    End If
    itemCount = SendMessage(hwndListBox, LB_GETCOUNT, IntPtr.Zero, IntPtr.Zero)
    numItems.Text = "" & itemCount.ToString()
End Sub
Private Sub DeleteText(ByVal sender As Object, ByVal args As EventArgs)
    selectedItem = SendMessage(listControl.hwndListBox, LB_GETCURSEL, IntPtr.Zero, IntPtr.Zero)
    If selectedItem <> -1 Then 'check for selected item
        SendMessage(hwndListBox, LB_DELETESTRING, New IntPtr(selectedItem), IntPtr.Zero)
    End If
    itemCount = SendMessage(hwndListBox, LB_GETCOUNT, IntPtr.Zero, IntPtr.Zero)
    numItems.Text = "" & itemCount.ToString()
End Sub
private void AppendText(object sender, EventArgs args)
{
  if (txtAppend.Text != string.Empty)
  {
    SendMessage(hwndListBox, LB_ADDSTRING, IntPtr.Zero, txtAppend.Text);
  }
  itemCount = SendMessage(hwndListBox, LB_GETCOUNT, IntPtr.Zero, IntPtr.Zero);
  numItems.Text = "" + itemCount.ToString();
}
private void DeleteText(object sender, EventArgs args)
{
  selectedItem = SendMessage(listControl.hwndListBox, LB_GETCURSEL, IntPtr.Zero, IntPtr.Zero);
  if (selectedItem != -1) //check for selected item
  {
    SendMessage(hwndListBox, LB_DELETESTRING, (IntPtr)selectedItem, IntPtr.Zero);
  }
  itemCount = SendMessage(hwndListBox, LB_GETCOUNT, IntPtr.Zero, IntPtr.Zero);
  numItems.Text = "" + itemCount.ToString();
}

Cuando el usuario selecciona un elemento que cambia su selección, el control se lo notifica a la ventana host enviándole un mensaje WM_COMMAND, que provoca el evento MessageHook para la página. El controlador recibe la misma información que el procedimiento de la ventana principal de la ventana host. Pasa también una referencia a un valor booleano, handled. handled se establece en true para indicar que se ha controlado el mensaje, y ya no hace falta más procesamiento.

WM_COMMAND se envía por varias razones, por lo que debe examinar el identificador de la notificación para determinar si es un evento que desea controlar. El identificador se incluye en los bytes más significativos del parámetro wParam. Como Microsoft .NET no tiene una macro HIWORD, en el ejemplo se utilizan operadores bit a bit para extraer el identificador. Si el usuario ha realizado o cambiado su selección, el identificador será LBN_SELCHANGE.

Cuando se recibe LBN_SELCHANGE, el ejemplo obtiene el índice del elemento seleccionado enviando un mensaje LB_GETCURSEL al control. Para obtener el texto, primero creará StringBuilder. A continuación, enviará un mensaje LB_GETTEXT al control. Pase el objeto StringBuilder vacío como parámetro wParam. Cuando SendMessage termine de procesarse, StringBuilder contendrá el texto del elemento seleccionado. Este uso de SendMessage requiere una nueva declaración PInvoke.

Por último, establezca handled en true para indicar que el mensaje se ha controlado.

Vea también

Referencia

HwndHost

Conceptos

Interoperabilidad de WPF y Win32

Tutorial: Introducción a WPF