Compartir a través de


Tutorial: Hospedar un control Win32 en WPF

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

Este tema le guía a través de una aplicación, Hospedaje de un control ListBox de Win32 en el ejemplo de WPF, que hospeda un control de cuadro de lista Win32. Este procedimiento general se puede extender para hospedar cualquier ventana win32.

Requisitos

En este tema se presupone una familiaridad básica con la programación de la API de WINDOWS y WPF. Para obtener una introducción básica a la programación de WPF, consulte Introducción. Para obtener una introducción a la programación de la API de Windows, consulta cualquiera de los numerosos libros sobre el tema, en particular Programación de Windows , de Charles Petzold.

Dado que el ejemplo que acompaña a este tema se implementa en C#, usa Servicios de invocación de plataforma (PInvoke) para acceder a la API de Windows. Cierta familiaridad con PInvoke es útil, pero no esencial.

Nota:

En este tema se incluyen varios ejemplos de código del ejemplo asociado. Sin embargo, para mejorar la legibilidad, no incluye el código de ejemplo completo. Puede obtener o ver el código completo del hospedaje de un control ListBox win32 en el ejemplo de WPF.

Procedimiento básico

En esta sección se describe el procedimiento básico para hospedar una ventana Win32 en una página de WPF. Las secciones restantes pasan por los detalles de cada paso.

El procedimiento de hospedaje básico es:

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

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

  3. En esa clase, invalide el miembro HwndHostde BuildWindowCore clase .

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

  5. Una vez creada la ventana host, devuelva el HWND de la ventana hospedada. Si quiere alojar uno o varios controles Win32, normalmente crea una ventana de anfitrión como elemento secundario del HWND y coloca los controles como elementos secundarios de esa ventana de anfitrión. Envolver los controles en una ventana host proporciona una manera sencilla para que la página de WPF reciba notificaciones de los controles, lo cual aborda algunos problemas específicos de Win32 relacionados con las notificaciones en la interfaz de HWND.

  6. Gestione los mensajes seleccionados enviados a la ventana anfitriona, como las notificaciones de los controles secundarios. Esto se puede hacer de dos maneras.

    • Si prefiere gestionar 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 de clase HwndHost en el MessageHook código subyacente. Este evento se produce para cada mensaje recibido por la ventana hospedada. Si eliges esta opción, todavía debes sobrescribir WndProc, pero solo necesitas una implementación mínima.

  7. Sobrescriba los métodos DestroyWindowCore y WndProc de HwndHost. Debe invalidar estos métodos para satisfacer el HwndHost contrato, pero es posible que solo tenga que proporcionar una implementación mínima.

  8. En el archivo de código subyacente, cree una instancia de la clase de hospedaje de controles y consíjala como elemento secundario del Border elemento que está pensado para hospedar la ventana.

  9. Para comunicarse con la ventana hospedada, envíe mensajes de Microsoft Windows y controle mensajes desde 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 lado izquierdo de la página hospeda varios controles WPF que proporcionan una interfaz de usuario (UI) que 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 sencillo. El elemento raíz es un DockPanel que tiene dos elementos secundarios. El primero es un Border elemento que hospeda el control ListBox. Ocupa un cuadrado de 200x200 en la esquina superior derecha de la página. El segundo es un StackPanel elemento que contiene un conjunto de controles WPF que muestran información y le permiten manipular el control ListBox estableciendo propiedades de interoperación expuestas. Para cada uno de los elementos secundarios de StackPanel, vea el material de referencia de los distintos elementos usados para obtener más información sobre lo que son estos elementos o lo que hacen, estos se enumeran en el código de ejemplo siguiente, pero no se explicarán aquí (el modelo de interoperación básico no requiere ninguno de ellos, se proporcionan para agregar alguna 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 hospeda realmente el control, ControlHost.cs. Hereda de HwndHost. El constructor toma dos parámetros, alto y ancho, que corresponden al alto y ancho del Border elemento que hospeda el control ListBox. Estos valores se usan más adelante para asegurarse de que el tamaño del control coincide con el Border elemento.

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

También hay un conjunto de constantes. Estas constantes se toman en gran medida de Winuser.h y permiten usar nombres convencionales al llamar a funciones 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

Invalida este método para crear la ventana 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 realmente aloja la página WPF. El control ListBox se crea como elemento secundario de esa ventana.

El motivo de este enfoque es simplificar el proceso de recepción de notificaciones del control. La HwndHost clase permite procesar los mensajes enviados a la ventana que hospeda. Si hospeda directamente un control Win32, recibirá los mensajes enviados al bucle de mensajes interno del control. Puede mostrar el control y enviarlos mensajes, pero no recibe las notificaciones que el control envía a su ventana primaria. Esto significa, entre otras cosas, que no tiene forma de detectar cuándo el usuario interactúa con el control. En su lugar, 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 por el control. Por comodidad, dado que la ventana host es poco más que un contenedor simple para el control, el paquete se denominará control ListBox.

Crear la ventana host y el control ListBox

Puede usar PInvoke para crear una ventana host para el control creando y registrando una clase de ventana, etc. Sin embargo, un enfoque mucho más sencillo es 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 elemento secundario de la ventana host. El alto y el ancho de ambas ventanas se establecen en los valores pasados al constructor, descritos anteriormente. Esto garantiza que el tamaño de la ventana de alojamiento y el control sea idéntico al área reservada en la página. Una vez creadas las ventanas, el ejemplo devuelve un HandleRef objeto 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

Implementación de DestroyWindow y WndProc

Además de BuildWindowCore, también debe sobrescribir los métodos WndProc y DestroyWindowCore del HwndHost. En este ejemplo, el MessageHook controlador controla los mensajes del control, 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 controló y devuelva 0. Para DestroyWindowCore, simplemente destruye 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

Alojar el control en la página

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

El ejemplo adjunta un controlador al MessageHook evento de ControlHost para recibir mensajes 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 encapsula el control ListBox real, incluidas las notificaciones del control. El ejemplo llama a SendMessage para obtener información del control y modificar su contenido. Los detalles de cómo se comunica la página con el control se describen en la sección siguiente.

Nota:

Observe que hay dos declaraciones PInvoke para SendMessage. Esto es necesario porque uno usa el wParam parámetro para pasar una cadena y el otro lo usa para pasar un entero. Necesita una declaración independiente para cada firma para asegurarse de que los datos se tramitan 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, envíe mensajes de Windows. El control le notifica cuando el usuario interactúa con él mediante el envío de notificaciones a su ventana host. El ejemplo de hospedaje de un control ListBox Win32 en WPF incluye una interfaz de usuario que proporciona varios ejemplos de cómo funciona esto:

  • Anexe un elemento a la lista.

  • Eliminar el elemento seleccionado de la lista

  • Muestra el texto del elemento seleccionado actualmente.

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

El usuario también puede seleccionar un elemento en el cuadro de lista haciendo clic en él, como lo haría para una aplicación Win32 convencional. Los datos mostrados se actualizan cada vez que el usuario cambia el estado del cuadro de lista seleccionando, agregando o anexando un elemento.

Para anexar elementos, envíe un LB_ADDSTRING mensaje al cuadro de lista. Para eliminar elementos, envíe LB_GETCURSEL para obtener el índice de la selección actual y, a continuación LB_DELETESTRING , para eliminar el elemento. El ejemplo también envía LB_GETCOUNTy usa el valor devuelto para actualizar la presentación que muestra el número de elementos. Ambas instancias de SendMessage usan una de las declaraciones de PInvoke que se describen 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 o cambia su selección, el control notifica a la ventana host mediante el envío de un WM_COMMAND mensaje, que genera el MessageHook evento de la página. El controlador recibe la misma información que el procedimiento de ventana principal de la ventana anfitriona. También pasa una referencia a un valor booleano, handled. Se establece handled en true para indicar que ha controlado el mensaje y no se necesita ningún procesamiento adicional.

WM_COMMAND se envía por diversos motivos, por lo que debe examinar el identificador de notificación para determinar si es un evento que desea controlar. El identificador se encuentra en la palabra alta del wParam parámetro . En el ejemplo se usan operadores bit a bit para extraer el identificador. Si el usuario ha realizado o cambiado su selección, el identificador será LBN_SELCHANGE.

Cuando LBN_SELCHANGE se recibe, el ejemplo obtiene el índice del elemento seleccionado enviando un mensaje al control LB_GETCURSEL. Para obtener el texto, primero debe crear un StringBuilder. Después, envíe un mensaje al control LB_GETTEXT. Pase el objeto vacío StringBuilder como parámetro wParam . Cuando SendMessage se devuelve, 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.

Consulte también