Share via


Tutorial: Hospedamiento de un control de Win32 en WPF

Windows Presentation Foundation (WPF) proporciona un entorno enriquecido para crear aplicaciones. Pero si ha realizado una inversión sustancial en código de Win32, puede ser más efectivo reutilizar al menos parte de ese código en la aplicación WPF, en lugar de volver a escribirlo por completo. WPF proporciona un mecanismo sencillo para hospedar una ventana de Win32 en una página de WPF.

Este tema le guiará a través de una aplicación (Hosting a Win32 ListBox Control in WPF Sample [Ejemplo de cómo hospedar un control ListBox de Win32 en WPF]) que hospeda un control de cuadro de lista de Win32. Este procedimiento general se puede extender para hospedar cualquier ventana de Win32.

Requisitos

En este tema se da por supuesto un conocimiento básico de la programación en WPF y API de Windows. Para obtener una introducción básica a la programación de WPF, vea Introducción. Para obtener una introducción a la programación en API de Windows, vea cualquiera de los numerosos libros publicados sobre el tema, en particular Programming Windows (Programación para Windows), escrito por Charles Petzold.

Dado que el ejemplo que acompaña a este tema se implementa en C#, hace uso de Servicios de invocación de plataforma (PInvoke) para obtener acceso a la API de Windows. Tener un conocimiento de PInvoke es útil, pero no esencial.

Nota:

En este tema se incluye una serie de ejemplos de código del ejemplo asociado. Sin embargo, para una mejor lectura, no se incluye el código de ejemplo completo. Puede obtener o ver el código completo de Hosting a Win32 ListBox Control in WPF Sample (Ejemplo de cómo hospedar un control ListBox de Win32 en WPF).

Procedimiento básico

En esta sección se describe el procedimiento básico para hospedar una ventana de Win32 en una página de WPF. En las secciones restantes 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 consiste en crear un elemento Border a fin de reservar una sección de la página para la ventana hospedada.

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

  3. En esa clase, invalide el miembro de clase BuildWindowCore de HwndHost.

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

  5. Una vez que haya creado la ventana host, devuelva el HWND de la ventana hospedada. Si quiere hospedar uno o más controles de Win32, debe crear una ventana host como elemento secundario del HWND y convertir los controles en elementos secundarios de esa ventana host. El ajuste de los controles en una ventana host proporciona una manera sencilla para que la página de WPF reciba notificaciones de los controles, lo que resuelve algunos problemas específicos de Win32 con las notificaciones más allá de los límites de HWND.

  6. Controle los mensajes seleccionados que se envían a la ventana host, como las notificaciones de los controles secundarios. Existen dos formas de hacerlo.

    • 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 solo 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 puede que solo deba proporcionar una implementación mínima.

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

  9. Comuníquese con la ventana hospedada. Para ello, envíe mensajes de Microsoft Windows y controle 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 de WPF que hospeda el control ListBox consta de dos regiones. El lado izquierdo de la página hospeda varios controles de WPF que proporcionan una interfaz de usuario que permite manipular el control de Win32. La esquina superior derecha de la página tiene una región cuadrada para el control ListBox.

El código para implementar este diseño es bastante sencillo. 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 cuadrado de 200x200 en la esquina superior derecha de la página. El segundo es un elemento StackPanel que contiene un conjunto de controles de WPF que muestran información y permiten manipular el control ListBox mediante el establecimiento de propiedades de interoperación expuestas. Para cada uno de los elementos secundarios de StackPanel, consulte el material de referencia sobre los distintos elementos usados para obtener información sobre lo que son y lo que hacen. Se muestran en el ejemplo de código siguiente, pero no se explican aquí (el modelo básico de interoperación no requiere ninguno de ellos, pero se proporcionan para agregar cierta interactividad al ejemplo).

<Window
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://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 de Microsoft Win32

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

public class ControlHost : HwndHost
{
  IntPtr hwndControl;
  IntPtr hwndHost;
  int hostHeight, hostWidth;

  public ControlHost(double height, double width)
  {
    hostHeight = (int)height;
    hostWidth = (int)width;
  }
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

Hay también un conjunto de constantes. Estas constantes suelen tomarse de Winuser.h y permiten usar nombres convencionales al llamar a funciones de Win32.

internal const int
  WS_CHILD = 0x40000000,
  WS_VISIBLE = 0x10000000,
  LBS_NOTIFY = 0x00000001,
  HOST_ID = 0x00000002,
  LISTBOX_ID = 0x00000001,
  WS_VSCROLL = 0x00200000,
  WS_BORDER = 0x00800000;
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

Invalidar BuildWindowCore para crear la ventana de Microsoft Win32

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

El motivo de esto es simplificar el proceso de recepción de notificaciones desde el control. La clase HwndHost permite procesar los mensajes enviados a la ventana que hospeda. Si hospeda un control de Win32 directamente, recibirá los mensajes enviados al bucle de mensajes internos del control. Puede mostrar el control y enviarle mensajes, pero no recibirá las notificaciones que el control envíe a su ventana primaria. Entre otras cosas, esto significa que no hay manera de detectar en qué momento el usuario interactúa con el control. Por eso, debe crear una ventana host y convertir el control en un elemento secundario de esa ventana. Esto le permite procesar los mensajes para la ventana host, incluidas las notificaciones que el control envía a la ventana. Para mayor comodidad, y dado que la ventana host es poco más que un simple contenedor del control, se hará referencia al paquete como un control ListBox.

Crear la ventana host y el control ListBox

Puede usar PInvoke para crear una ventana host para el control. Para ello, cree y registre una clase de ventana. Aun así, un enfoque mucho más sencillo consiste en crear una ventana con la clase de ventana "estática" predefinida. Esto le proporciona el procedimiento de ventana que necesita para recibir notificaciones del control y requiere una codificación mínima.

El HWND del control se expone a través de una propiedad de solo lectura, de modo que la página host pueda usarla para enviar mensajes al control.

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

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

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);
}
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
//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);
'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

Implementar DestroyWindow y WndProc

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

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);
}
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
[DllImport("user32.dll", EntryPoint = "DestroyWindow", CharSet = CharSet.Unicode)]
internal static extern bool DestroyWindow(IntPtr hwnd);
<DllImport("user32.dll", EntryPoint := "DestroyWindow", CharSet := CharSet.Unicode)>
Friend Shared Function DestroyWindow(ByVal hwnd As IntPtr) As Boolean
End Function

Hospedar el control en la página

Para hospedar el control en la página, primero debe crear una instancia de la clase ControlHost. Pase el alto y el ancho del elemento Border que contiene el control (ControlHostElement) al constructor ControlHost. Esto garantiza que el control ListBox tenga el tamaño correcto. Después, hospede el control en la página mediante la asignación del objeto ControlHost a la propiedad Child del host Border.

En el ejemplo se adjunta un controlador al evento MessageHook de ControlHost para recibir mensajes procedentes del control. Este evento se genera para cada mensaje enviado a la ventana hospedada. En este caso, estos 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. En la siguiente sección se describe en detalle la manera en que la página se comunica con el control.

Nota:

Observe que hay dos declaraciones PInvoke para SendMessage. Esto es necesario porque una usa el parámetro wParam para pasar una cadena y la otra lo usa para pasar un entero. Necesita una declaración distinta para cada firma para asegurarse de que los datos se serializan correctamente.

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();
}
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

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);

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

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

Para manipular el control, debe enviarle mensajes de Windows. El control le notifica en qué momento el usuario interactúa con él mediante el envío de notificaciones a la ventana host. En Hosting a Win32 ListBox Control in WPF (Ejemplo de cómo hospedar un control ListBox de Win32 en WPF) se incluye una interfaz de usuario que proporciona varios ejemplos de cómo funciona esto:

  • Anexar un elemento a la lista

  • Eliminar el elemento seleccionado de la lista

  • Mostrar el texto del elemento actualmente seleccionado

  • Mostrar el número de elementos de la lista

El usuario también puede seleccionar un elemento del cuadro de lista si hace clic en él, como haría con una aplicación de Win32 convencional. Los datos mostrados se actualizan cada vez que el usuario cambia el estado del cuadro de lista al seleccionar, agregar o anexar un elemento.

Para anexar elementos, envíe al cuadro de lista un mensaje LB_ADDSTRING. Si quiere 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 también se envía LB_GETCOUNT y se usa el valor devuelto para actualizar la pantalla que muestra el número de elementos. Ambas instancias de SendMessage usan una de las declaraciones PInvoke descritas en la sección anterior.

private void AppendText(object sender, EventArgs args)
{
  if (!string.IsNullOrEmpty(txtAppend.Text))
  {
    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();
}
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

Cuando el usuario selecciona un elemento y cambia su selección, el control se lo notifica a la ventana host mediante el envío de un mensaje WM_COMMAND, que genera el evento MessageHook para la página. El controlador recibe la misma información que el procedimiento de ventana principal de la ventana host. También pasa una referencia a un valor booleano, handled. Establezca handled en true para indicar que ha controlado el mensaje y que no es necesario ningún procesamiento adicional.

WM_COMMAND se envía por diversas razones, por lo que debe examinar el identificador de la notificación para determinar si se trata de un evento que quiere controlar. El identificador se encuentra en el byte más significativo del parámetro wParam. En el ejemplo se usan los operadores bit a bit para extraer el identificador. Si el usuario ha realizado su selección o la ha cambiado, el identificador será LBN_SELCHANGE.

Cuando se recibe LBN_SELCHANGE, el ejemplo obtiene el índice del elemento seleccionado mediante el envío de un mensaje LB_GETCURSEL al control. Para obtener el texto, primero debe crear un StringBuilder. Después, envíe al control un mensaje LB_GETTEXT. Pase el objeto StringBuilder vacío como el parámetro wParam. Cuando se devuelve SendMessage, StringBuilder contendrá el texto del elemento seleccionado. Este uso de SendMessage requiere otra declaración PInvoke.

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

Vea también