Partilhar via


Passo a passo: Hospedar um controle Win32 no WPF

Windows Presentation Foundation (WPF) fornece um ambiente avançado para a criação de aplicativos. No entanto, quando você tem um investimento substancial no código Win32, pode ser mais eficaz reutilizar pelo menos parte desse código em seu aplicativo WPF em vez de reescrevê-lo completamente. WPF fornece um mecanismo simples para hospedar uma janela Win32, em uma página WPF.

Este tópico orienta você por um aplicativo, Hospedando um controle Win32 ListBox no exemplo WPF, que hospeda um controle de caixa de listagem Win32. Este procedimento geral pode ser estendido para hospedar qualquer janela do Win32.

Requerimentos

Este tópico pressupõe uma familiaridade básica com a programação do WPF e da API do Windows. Para obter uma introdução básica à programação WPF, consulte Introdução. Para uma introdução à programação da API do Windows, consulte qualquer um dos inúmeros livros sobre o assunto, em particular Programming Windows de Charles Petzold.

Como o exemplo que acompanha este tópico é implementado em C#, ele usa o Platform Invocation Services (PInvoke) para acessar a API do Windows. Alguma familiaridade com o PInvoke é útil, mas não essencial.

Observação

Este tópico inclui vários exemplos de código do exemplo associado. No entanto, para facilitar a leitura, ele não inclui o código de exemplo completo. Você pode obter ou exibir o código completo de Hospedando um controle Win32 ListBox no exemplo WPF.

Procedimento de base

Esta seção descreve o procedimento básico para hospedar uma janela do Win32 em uma página WPF. As seções restantes passam pelos detalhes de cada etapa.

O procedimento básico de hospedagem é:

  1. Implemente uma página WPF para hospedar a janela. Uma técnica é criar um Border elemento para reservar uma seção da página para a janela hospedada.

  2. Implemente uma classe para hospedar o controle que herda do HwndHost.

  3. Nessa classe, substitua o membro da classe HwndHostBuildWindowCore.

  4. Crie a janela hospedada como um filho da janela que contém a página WPF. Embora a programação WPF convencional não precise fazer uso explícito dele, a página de hospedagem é uma janela com um handle (HWND). Você recebe o HWND da página através do parâmetro hwndParent do método BuildWindowCore. A janela hospedada deve ser criada como um filho deste HWND.

  5. Depois de criar a janela do host, retorne o HWND da janela hospedada. Se você quiser hospedar um ou mais controles Win32, normalmente cria uma janela de host como um filho do HWND e torna os controles filhos dessa janela de host. Envolver os controles em uma janela de host fornece uma maneira simples para sua página WPF receber notificações dos controles, que lida com alguns problemas específicos do Win32 com notificações através do limite HWND.

  6. Manipule mensagens selecionadas enviadas para a janela do host, como notificações de controles filho. Há duas maneiras de fazer isso.

    • Se preferires manipular mensagens na tua classe de hospedagem, sobrescreve o método WndProc da classe HwndHost.

    • Se você preferir que o WPF manipule as mensagens, manipule o HwndHost evento class MessageHook em seu code-behind. Esse evento ocorre para cada mensagem recebida pela janela hospedada. Se você escolher essa opção, ainda deverá substituir WndProc, mas precisará apenas de uma implementação mínima.

  7. Substitua os métodos DestroyWindowCore e WndProc de HwndHost. Você deve substituir esses métodos para satisfazer o HwndHost contrato, mas talvez só precise fornecer uma implementação mínima.

  8. Em seu arquivo code-behind, crie uma instância da classe de hospedagem de controle e torne-a um filho do Border elemento que se destina a hospedar a janela.

  9. Comunique-se com a janela hospedada enviando-lhe mensagens do Microsoft Windows e manipulando mensagens de suas janelas filhas, como notificações enviadas por controles.

Implementar o layout de página

O layout para a página WPF que hospeda o controle ListBox consiste em duas regiões. O lado esquerdo da página hospeda vários controles WPF que fornecem uma interface do usuário (UI) que permite manipular o controle Win32. O canto superior direito da página tem uma região quadrada para o controle ListBox hospedado.

O código para implementar este layout é bastante simples. O elemento raiz é um DockPanel que tem dois elementos filho. O primeiro é um Border elemento que hospeda o controle ListBox. Ocupa um quadrado de 200x200 no canto superior direito da página. O segundo é um StackPanel elemento que contém um conjunto de controles WPF que exibem informações e permitem que você manipule o controle ListBox definindo propriedades de interoperação expostas. Para cada um dos elementos que são filhos do StackPanel, veja o material de referência para os vários elementos usados para detalhes sobre o que esses elementos são ou o que eles fazem, estes estão listados no código de exemplo abaixo, mas não serão explicados aqui (o modelo básico de interoperação não requer nenhum deles, eles são fornecidos para adicionar alguma interatividade à amostra).

<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 uma classe para hospedar o controle Microsoft Win32

O núcleo deste exemplo é a classe que realmente hospeda o controle, ControlHost.cs. Herda de HwndHost. O construtor usa dois parâmetros, altura e largura, que correspondem à altura e largura do Border elemento que hospeda o ListBox controle. Esses valores são usados posteriormente para garantir que o tamanho do controle corresponda ao 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

Há também um conjunto de constantes. Essas constantes são em grande parte retiradas de Winuser.h, e permitem que você use nomes convencionais ao chamar funções 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

Substitua BuildWindowCore para criar a janela do Microsoft Win32

Você substitui esse método para criar a janela do Win32 que será hospedada pela página e fazer a conexão entre a janela e a página. Como este exemplo envolve a hospedagem de um controle ListBox, duas janelas são criadas. A primeira é a janela que é realmente hospedada pela página WPF. O controle ListBox é criado como um filho dessa janela.

A razão para esta abordagem é simplificar o processo de receber notificações do controlo. A HwndHost classe permite que você processe mensagens enviadas para a janela que está hospedando. Se você hospedar um controle Win32 diretamente, você recebe as mensagens enviadas para o loop de mensagem interna do controle. Você pode mostrar o controlo e enviar-lhe mensagens, mas não recebe as notificações que o controlo envia para a janela-mãe. Isso significa, entre outras coisas, que você não tem como detetar quando o usuário interage com o controle. Em vez disso, crie uma janela anfitriã e configure o controlo como um elemento subordinado dessa janela. Isso permite que você processe as mensagens para a janela do host, incluindo as notificações enviadas a ele pelo controle. Por conveniência, como a janela do host é pouco mais do que um simples wrapper para o controle, o pacote será referido como um controle ListBox.

Criar a janela de anfitrião e o controlo ListBox

Você pode usar PInvoke para criar uma janela anfitriã para o controlo criando e registando uma classe de janela, entre outros passos necessários. No entanto, uma abordagem muito mais simples é criar uma janela com a classe de janela "estática" predefinida. Isso fornece o procedimento de janela que você precisa para receber notificações do controle e requer codificação mínima.

O HWND do controle é exposto por meio de uma propriedade somente leitura, de modo que a página do host pode usá-lo para enviar mensagens para o controle.

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

O controlo ListBox é criado como filho da janela do host. A altura e a largura de ambas as janelas são definidas para os valores passados para o construtor, conforme discutido acima. Isso garante que o tamanho da janela do anfitrião e do controlador seja idêntico à área reservada na página. Depois que as janelas são criadas, o exemplo retorna um HandleRef objeto que contém o HWND da janela do 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 e WndProc

Além do BuildWindowCore, deve substituir também os métodos WndProc e DestroyWindowCore do HwndHost. Neste exemplo, as mensagens para o controle são manipuladas pelo MessageHook manipulador, portanto, a implementação de WndProc e DestroyWindowCore é mínima. No caso de WndProc, defina handled para false para indicar que a mensagem não foi manipulada e retorne 0. Para DestroyWindowCore, basta destruir a janela.

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 o controle na página

Para hospedar o controle na página, primeiro crie uma nova instância da ControlHost classe. Passe a altura e a largura do elemento de borda que contém o controlo (ControlHostElement) para o construtor ControlHost. Isso garante que o ListBox seja dimensionado corretamente. Em seguida, você acolhe o controlo na página ao atribuir o objeto ControlHost à propriedade Child do anfitrião Border.

O exemplo anexa um manipulador ao MessageHook evento do ControlHost para receber mensagens do controle. Esse evento é gerado para cada mensagem enviada para a janela hospedada. Nesse caso, essas são as mensagens enviadas para a janela que encapsula o controle ListBox real, incluindo notificações do controle. O exemplo chama SendMessage para obter informações do controle e modificar seu conteúdo. Os detalhes de como a página se comunica com o controle são discutidos na próxima seção.

Observação

Observe que há duas declarações PInvoke para SendMessage. Isso é necessário porque um usa o wParam parâmetro para passar uma cadeia de caracteres e o outro o usa para passar um inteiro. Você precisa de uma declaração separada para cada assinatura para garantir que os dados sejam empacotados corretamente.

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 a comunicação entre o controle e a página

Você manipula o controle enviando-lhe mensagens do Windows. O controle notifica quando o usuário interage com ele, enviando notificações para sua janela de host. O exemplo Hosting a Win32 ListBox Control in WPF inclui uma interface do usuário que fornece vários exemplos de como isso funciona:

  • Acrescentar um item à lista.

  • Excluir o item selecionado da lista

  • Exiba o texto do item selecionado no momento.

  • Exiba o número de itens na lista.

O usuário também pode selecionar um item na caixa de listagem clicando nele, assim como faria para um aplicativo Win32 convencional. Os dados exibidos são atualizados sempre que o usuário altera o estado da caixa de listagem selecionando, adicionando ou anexando um item.

Para acrescentar itens, envie uma LB_ADDSTRING mensagem à caixa de listagem. Para excluir itens, envie LB_GETCURSEL para obter o índice da seleção atual e, em seguida, LB_DELETESTRING exclua o item. O exemplo também envia LB_GETCOUNTe usa o valor retornado para atualizar a exibição que mostra o número de itens. Ambas as instâncias de SendMessage usam uma das declarações PInvoke discutidas na seção 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

Quando o usuário seleciona um item ou altera sua seleção, o controle notifica a janela do host enviando-lhe uma WM_COMMAND mensagem, que gera o MessageHook evento para a página. O manipulador recebe as mesmas informações que o procedimento da janela principal da janela anfitriã. Ele também passa uma referência a um valor booleano, handled. Você define handled como true para indicar que a mensagem foi tratada e que não é necessário processamento adicional.

WM_COMMAND é enviado por vários motivos, portanto, você deve examinar a ID de notificação para determinar se é um evento que deseja manipular. O ID está contido na palavra alta do wParam parâmetro. O exemplo usa operadores de bit a bit para extrair o ID. Se o usuário tiver feito ou alterado sua seleção, o ID será LBN_SELCHANGE.

Quando LBN_SELCHANGE é recebido, o exemplo obtém o índice do item selecionado enviando uma LB_GETCURSEL mensagem ao controle. Para obter o texto, primeiro crie um arquivo StringBuilder. Em seguida, você envia uma LB_GETTEXT mensagem ao controle. Passe o objeto vazio StringBuilder como parâmetro wParam . Quando SendMessage retorna, o StringBuilder conterá o texto do item selecionado. Esta utilização de SendMessage requer ainda outra declaração PInvoke.

Finalmente, defina handled como true indicando que a mensagem foi tratada.

Ver também