Share via


Demonstra Passo a passo: Hospedar um controle Win32 no WPF

O Windows Presentation Foundation (WPF) fornece um ambiente avançado para a criação de aplicativos. No entanto, quando você tem um investimento substancial em 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 direto 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.

Requisitos

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 do WPF, consulte Introdução. Para obter uma introdução à programação de API do Windows, consulte qualquer um dos vários 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 PInvoke (Platform Invocation Services) para acessar a API do Windows. Alguma familiaridade com o PInvoke é útil, mas não essencial.

Observação

Este tópico inclui alguns exemplos de código da amostra associada. No entanto, para facilitar a leitura, não inclui o código de exemplo completo. É possível obter ou exibir o código completo de Amostra de hospedando um controle Win32 ListBox no WPF.

O procedimento básico

Esta seção descreve o procedimento básico para hospedar uma janela Win32 em uma página WPF. As seções restantes apresentam 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 BuildWindowCoreda HwndHost classe .

  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 dela, a página de hospedagem é uma janela com um identificador (HWND). Você recebe a página HWND através do hwndParent parâmetro do BuildWindowCore método. A janela hospedada deve ser criada como um filho desse HWND.

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

  6. Manipule as mensagens selecionadas enviadas à janela de host, como notificações dos controles filho. Há duas maneiras de fazer isso.

    • Se você preferir manipular mensagens em sua classe de hospedagem, substitua o WndProcHwndHost método da classe.

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

  7. Substitua os métodos e WndProc de DestroyWindowCoreHwndHost. Você deve substituir esses métodos para satisfazer o HwndHost contrato, mas talvez seja necessário fornecer apenas uma implementação mínima.

  8. No 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 da página

O layout da 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 esse layout é bastante simples. O elemento raiz é um DockPanel que tem dois elementos filho. O primeiro é um Border elemento que hospeda o controle ListBox. Ele ocupa um quadrado de 200x200 no canto superior direito da página. O segundo é um elemento que contém um StackPanel 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 , consulte o material de referência para os vários elementos usados para obter detalhes sobre o que esses elementos são ou o que eles fazem, eles estão listados no código de exemplo abaixo, mas não serão explicados aqui (o modelo básico de StackPanelinteroperação não requer nenhum deles, eles são fornecidos para adicionar alguma interatividade ao exemplo).

<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 dessa amostra é a classe que realmente hospeda o controle: ControlHost.cs. Herda de HwndHost. O construtor usa dois parâmetros, height e width, que correspondem à altura e largura do elemento que hospeda Border o controle ListBox. 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

Também existe 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

Substituir o BuildWindowCore para criar a janela do Microsoft Win32

Você substitui esse método para criar a janela Win32 que será hospedada pela página e fazer a conexão entre a janela e a página. Como essa amostra 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 essa abordagem é simplificar o processo de recebimento de notificações do controle. A HwndHost classe permite que você processe mensagens enviadas para a janela que está hospedando. Se você hospedar um controle Win32 diretamente, você receberá as mensagens enviadas para o loop de mensagem interno do controle. É possível exibir o controle e lhe enviar mensagens, mas você não receberá as notificações que o controle envia para a janela pai. Isso significa, entre outras coisas, que você não tem nenhuma maneira de detectar quando o usuário interage com o controle. Em vez disso, crie uma janela de host e transforme o controle em filho dessa janela. Assim, pode processar as mensagens para a janela de host, incluindo as notificações enviadas a ela pelo controle. Por uma questão de conveniência, como a janela de host é mais que um wrapper simples para o controle, o pacote será chamado de controle ListBox.

Criar a janela de host e o controle ListBox

Você pode usar PInvoke para criar uma janela de host para o controle criando e registrando uma classe de janela e assim por diante. No entanto, uma abordagem muito mais simples é criar uma janela com a classe de janela predefinida “estática”. Isso proporciona o procedimento de janela de que você precisa para receber notificações do controle e requer o mínimo de codificação.

O HWND do controle é exposto por meio de uma propriedade somente leitura; assim, a página de 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 controle ListBox é criado como um filho da janela de host. A altura e a largura de ambas as janelas são definidas para os valores passados para o construtor, discutidos acima. Isso garante que o tamanho da janela de host e do controle sejam idênticos à á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 , você também deve substituir os métodos e DestroyWindowCore do BuildWindowCoreHwndHost.WndProc 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 como false indicar que a mensagem não foi manipulada e retornar 0. Pois 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 border que contém o controle (ControlHostElement) para o ControlHost construtor. Isso garante que o ListBox será dimensionado corretamente. Em seguida, hospede o controle na página atribuindo o ControlHost objeto à Child propriedade do host Border.

O exemplo anexa um manipulador ao MessageHook evento do para receber mensagens do ControlHost controle. Esse evento é gerado para cada mensagem enviada para a janela hospedada. Nesse caso, são as mensagens enviadas à janela que encapsula o verdadeiro controle ListBox, 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 parâmetro para passar uma cadeia de caracteres e o outro o wParam usa para passar um inteiro. Você precisa de uma declaração separada para cada assinatura a fim de garantir que o marshaling dos dados será realizado 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 comunicação entre o controle e a página

Você manipula o controle enviando-lhe mensagens do Windows. Para notificar você de que o usuário interagiu com ele, o controle envia notificações para a janela de host. O exemplo de hospedagem de um controle ListBox Win32 no WPF inclui uma interface do usuário que fornece vários exemplos de como isso funciona:

  • Acrescentar um novo item à lista.

  • Excluir o item selecionado da lista

  • Exibir o texto do item selecionado atualmente.

  • Exibir 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 ao selecionar, adicionar ou acrescentar um item.

Para acrescentar itens, envie uma mensagem à caixa de LB_ADDSTRING listagem. Para excluir itens, envie LB_GETCURSEL para obter o índice da seleção atual e, em seguida, LB_DELETESTRING para excluir 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 uso de 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 de janela principal da janela de host. Também passa uma referência a um valor booliano: handled. Você define handled como true para indicar que você manipulou a mensagem e nenhum processamento adicional é necessário.

WM_COMMAND é enviado por vários motivos, portanto, você deve examinar a ID de notificação para determinar se é um evento que você deseja manipular. O ID está contido na palavra alta do wParam parâmetro. O exemplo usa operadores 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 StringBuilderarquivo . Em seguida, você envia uma LB_GETTEXT mensagem ao controle. Passe o objeto vazio StringBuilder como o wParam parâmetro. Quando SendMessage retorna, o conterá o StringBuilder texto do item selecionado. Esse uso de SendMessage requer ainda outra declaração PInvoke.

Finalmente, defina handled como true para indicar que a mensagem foi manipulada.

Confira também