Compartilhar via


Passo a passo: hospedar um controle Win32 no WPF

O WPF (Windows Presentation Foundation) 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. O WPF fornece um mecanismo simples para hospedar uma janela Do Win32 em uma página do WPF.

Este tópico orienta você por um aplicativo, Hospedando um controle ListBox Win32 em um exemplo de 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 da API do WPF e do Windows. Para obter uma introdução básica à programação do WPF, consulte Introdução. Para obter uma introdução à programação com a API do Windows, consulte qualquer um dos inúmeros livros sobre o assunto, em particular Programação 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 pInvoke é útil, mas não essencial.

Observação

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

O procedimento básico

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

O procedimento básico de hospedagem é:

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

  2. Implementar uma classe para hospedar o controle que herda de HwndHost.

  3. Nessa classe, substitua o membro HwndHost da classe BuildWindowCore.

  4. Crie a janela hospedada como um filho da janela que contém a página do WPF. Embora a programação convencional do WPF não precise fazer uso explícito dela, a página de hospedagem é uma janela com um identificador (HWND). Você recebe o HWND da página por meio do parâmetro hwndParent do método BuildWindowCore. 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. Para hospedar um ou mais controles do Win32, normalmente é criada uma janela de host como filho do HWND e os controles se tornam filhos dessa janela de host. Encapsular os controles em uma janela de host fornece uma maneira simples para sua página do WPF receber notificações dos controles, o que lida com alguns problemas específicos do Win32 com notificações no limite do HWND.

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

    • Se preferir manipular mensagens em sua classe de hospedagem, substitua o método WndProc da classe HwndHost.

    • Se você preferir que o WPF manipule as mensagens, manipule o evento HwndHost da classe MessageHook no seu code-behind. Esse evento ocorre para cada mensagem recebida pela janela hospedada. Caso escolha essa opção, você ainda precisará substituir o WndProc, mas será necessária somente uma implementação mínima.

  7. Substitua os métodos DestroyWindowCore e WndProc de HwndHost. Você deve substituir esses métodos para atender ao contrato de HwndHost, 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 elemento Border que se destina a hospedar a janela.

  9. Para se comunicar com a janela hospedada, envie mensagens do Microsoft Windows e manipule mensagens das janelas filho, como notificações enviadas pelos controles.

Implementar o layout da página

O layout da página do 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 (interface do usuário) 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 elemento Border que hospeda o Controle ListBox. Ocupa um quadrado 200x200 no canto superior direito da página. O segundo é um elemento StackPanel que contém um conjunto de controles WPF que exibem informações e permitem manipular o Controle ListBox definindo as propriedades de interoperação expostas. Para cada um dos elementos que são filhos do StackPanel, 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 são listados no código de exemplo abaixo, mas não serão explicados aqui (o modelo básico de interoperação não exige 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 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 elemento Border que hospeda o controle ListBox. Esses valores são usados posteriormente para garantir que o tamanho do controle corresponda ao 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

Há também um conjunto de constantes. Essas constantes são retiradas em grande parte do 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

Sobrescrever 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 este exemplo envolve a hospedagem de um Controle ListBox, duas janelas são criadas. A primeira é a janela que é realmente hospedada pela página do WPF. O Controle ListBox é criado como um filho dessa janela.

O motivo dessa abordagem é simplificar o processo de recebimento de notificações do controle. A classe HwndHost permite processar mensagens enviadas para a janela que está hospedando. Se você hospedar um controle Win32 diretamente, 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 como 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. Isso permite que você processe as mensagens da janela do host, incluindo as notificações enviadas a ela pelo controle. Por conveniência, como a janela anfitriã é pouco mais do que um invólucro simples para o controle, o pacote será referido como o controle ListBox.

Criar a janela de host e o controle ListBox

Você pode usar o PInvoke para criar uma janela de host para o controle por meio da criação e do registro de uma classe de janela e assim por diante. No entanto, uma abordagem muito mais simples é criar uma janela com a classe de janela "estática" predefinida. 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 como 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 objeto HandleRef 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 de BuildWindowCore, você também precisa substituir os métodos WndProc e DestroyWindowCore do HwndHost. Neste exemplo, as mensagens para o controle são tratadas pelo manipulador de MessageHook, portanto, a implementação de WndProc e DestroyWindowCore é mínima. No caso de WndProc, defina handled como false para indicar que a mensagem não foi tratada e retornar 0. Para o 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 classe ControlHost. Passe a altura e a largura do elemento de borda que contém o controle (ControlHostElement) para o construtor ControlHost. Isso garante que o ListBox seja dimensionado corretamente. Em seguida, você hospeda o controle na página atribuindo o objeto ControlHost à propriedade Child do host Border.

O exemplo anexa um manipulador ao evento MessageHook do ControlHost para receber mensagens do controle. Esse evento é gerado para cada mensagem enviada à 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 parâmetro wParam 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 processados 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 mensagens do Windows. O controle notifica você quando o usuário interage com ele enviando notificações para a janela do host. O exemplo Hospedando um controle Win32 ListBox no WPF inclui uma interface do usuário que fornece vários exemplos de como isso funciona:

  • Acrescente 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, da mesma forma que 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 acrescentando um item.

Para acrescentar itens, envie à caixa de listagem uma mensagem LB_ADDSTRING. Para excluir itens, envie LB_GETCURSEL para obter o índice da seleção atual e, em seguida, LB_DELETESTRING 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 utilizam uma das declarações de 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 mensagem WM_COMMAND, o que gera o evento MessageHook para a página. O manipulador recebe as mesmas informações que o procedimento de janela principal da janela de host. Ele também passa uma referência a um valor booliano, handled. Defina handled como true para indicar que você lidou com 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. A ID está contida na palavra alta do parâmetro wParam. O exemplo usa operadores bit a bit para extrair a ID. Se o usuário tiver feito ou alterado sua seleção, a ID será LBN_SELCHANGE.

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

Por fim, defina handled como true para indicar que a mensagem foi tratada.

Consulte também