Procedura dettagliata: Ospitare un controllo Win32 in WPF

Windows Presentation Foundation (WPF) offre un ambiente avanzato per la creazione di applicazioni. Tuttavia, quando si ha un notevole investimento nel codice Win32, potrebbe essere più efficace riutilizzare almeno un codice nell'applicazione WPF anziché riscriverlo completamente. WPF offre un meccanismo semplice per l'hosting di una finestra Win32 in una pagina WPF.

Questo argomento illustra in dettaglio un'applicazione, Hosting di un controllo ListBox Win32 nell'esempio WPF, che ospita un controllo casella di riepilogo Win32. Questa procedura generale può essere estesa all'hosting di qualsiasi finestra Win32.

Requisiti

In questo argomento si presuppone una familiarità di base con la programmazione api WPF e Windows. Per un'introduzione di base alla programmazione WPF, vedere Introduzione. Per un'introduzione alla programmazione api di Windows, vedi uno dei numerosi libri sull'argomento, in particolare Programmazione di Windows di Charles Petzold.

Poiché l'esempio che accompagna questo argomento viene implementato in C#, usa Platform Invocation Services (PInvoke) per accedere all'API Windows. Una certa familiarità con PInvoke è utile ma non essenziale.

Nota

Questo argomento include vari esempi di codice tratti dall'esempio associato. Tuttavia, per una questione di leggibilità, il codice di esempio completo non è compreso. È possibile ottenere o visualizzare il codice completo da Esempio di hosting di un controllo Win32 ListBox in WPF.

Procedura di base

Questa sezione descrive la procedura di base per l'hosting di una finestra Win32 in una pagina WPF. Le sezioni rimanenti illustrano in dettaglio i vari passaggi.

La procedura di hosting base è la seguente:

  1. Implementare una pagina WPF per ospitare la finestra. Una tecnica consiste nel creare un Border elemento per riservare una sezione della pagina per la finestra ospitata.

  2. Implementare una classe per ospitare il controllo che eredita da HwndHost.

  3. In tale classe eseguire l'override del membro BuildWindowCoredella HwndHost classe .

  4. Creare la finestra ospitata come figlio della finestra che contiene la pagina WPF. Anche se la programmazione WPF convenzionale non deve usarla in modo esplicito, la pagina di hosting è una finestra con un handle (HWND). Si riceve la pagina HWND tramite il hwndParent parametro del BuildWindowCore metodo . La finestra ospitata deve essere creata come elemento figlio dell'oggetto HWND.

  5. Dopo aver creato la finestra host, restituire l'oggetto HWND della finestra ospitata. Se vuoi ospitare uno o più controlli Win32, in genere crei una finestra host come figlio di HWND e crei gli elementi figlio dei controlli di tale finestra host. Il wrapping dei controlli in una finestra host consente alla pagina WPF di ricevere notifiche dai controlli, che si occupa di alcuni problemi Win32 specifici con le notifiche oltre il limite HWND.

  6. Gestire i messaggi selezionati inviati alla finestra host, ad esempio notifiche dai controlli figlio. Per eseguire questa operazione è possibile procedere in due modi:

    • Se si preferisce gestire i messaggi nella classe di hosting, eseguire l'override del WndProc metodo della HwndHost classe .

    • Se si preferisce che WPF gestisca i messaggi, gestire l'evento HwndHost di classe MessageHook nel code-behind. Questo evento si verifica per ogni messaggio ricevuto dalla finestra ospitata. Se si sceglie questa opzione, è comunque necessario eseguire l'override di , ma è necessaria solo un'implementazione WndProcminima.

  7. Eseguire l'override dei DestroyWindowCore metodi e WndProc di HwndHost. È necessario eseguire l'override di questi metodi per soddisfare il HwndHost contratto, ma potrebbe essere necessario fornire solo un'implementazione minima.

  8. Nel file code-behind creare un'istanza della classe host del controllo e impostarla come figlio dell'elemento Border che deve ospitare la finestra.

  9. Comunicare con la finestra ospitata inviando messaggi di Microsoft Windows e gestendo i messaggi dalle finestre figlio, ad esempio le notifiche inviate dai controlli.

Implementare il layout di pagina

Il layout per la pagina WPF che ospita il controllo ListBox è costituito da due aree. Il lato sinistro della pagina ospita diversi controlli WPF che forniscono un'interfaccia utente che consente di modificare il controllo Win32. L'angolo superiore destro della pagina include un'area quadrata per il controllo ListBox ospitato.

Il codice per implementare questo layout è abbastanza semplice. L'elemento radice è un oggetto DockPanel con due elementi figlio. Il primo è un Border elemento che ospita il controllo ListBox. Occupa un quadrato 200x200 nell'angolo superiore destro della pagina. Il secondo è un StackPanel elemento che contiene un set di controlli WPF che visualizzano informazioni e consentono di modificare il controllo ListBox impostando proprietà di interoperabilità esposte. Per ognuno degli elementi figlio di StackPanel, vedere il materiale di riferimento per i vari elementi usati per informazioni dettagliate su cosa sono questi elementi o su cosa fanno, questi elementi sono elencati nel codice di esempio seguente, ma non verranno illustrati qui (il modello di interoperabilità di base non richiede alcuno di essi, vengono forniti per aggiungere alcune interattività all'esempio).

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

Implementare una classe per l'hosting del controllo Microsoft Win32

La parte centrale di questo esempio è la classe che ospita effettivamente il controllo, ControlHost.cs. Eredita da HwndHost. Il costruttore accetta due parametri, altezza e larghezza, che corrispondono all'altezza e alla larghezza dell'elemento Border che ospita il controllo ListBox. Questi valori vengono usati in un secondo momento per garantire che le dimensioni del controllo corrispondano all'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

È anche presente un insieme di costanti. Queste costanti sono in gran parte ricavate da Winuser.h e consentono di usare nomi convenzionali quando si chiamano le funzioni 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

Eseguire l'override di BuildWindowCore per creare la finestra Microsoft Win32

Si esegue l'override di questo metodo per creare la finestra Win32 che verrà ospitata dalla pagina e stabilire la connessione tra la finestra e la pagina. Poiché questo esempio include l'hosting di un controllo ListBox vengono create due finestre. La prima è la finestra effettivamente ospitata dalla pagina WPF. Il controllo ListBox viene creato come elemento figlio di questa finestra.

Lo scopo di questo approccio è semplificare il processo di ricezione di notifiche dal controllo. La HwndHost classe consente di elaborare i messaggi inviati alla finestra che ospita. Se si ospita direttamente un controllo Win32, si ricevono i messaggi inviati al ciclo di messaggi interno del controllo. È possibile visualizzare il controllo e inviare a esso messaggi ma non ricevere le notifiche che il controllo invia alla propria finestra padre. Ciò significa, tra l'altro, che non è possibile in alcun modo rilevare quando l'utente interagisce con il controllo. Creare invece una finestra host e impostare il controllo come elemento figlio di tale finestra. Ciò consente di elaborare i messaggi per la finestra host, incluse le notifiche inviate a essa dal controllo. Per comodità, dal momento che la finestra host è poco più di un semplice wrapper per il controllo, il pacchetto verrà restituito come un controllo ListBox.

Creare la finestra host e il controllo ListBox

È possibile usare PInvoke per creare una finestra host per il controllo creando e registrando una classe finestra e così via. Tuttavia, un approccio molto più semplice consiste nel creare una finestra con la classe della finestra "statica" predefinita. Questo approccio rende disponibile la procedura della finestra necessaria per ricevere le notifiche dal controllo e richiede codice minimo.

L'oggetto HWND del controllo è esposto tramite una proprietà di sola lettura di modo che la pagina host possa usarlo per inviare messaggi al controllo.

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

Il controllo ListBox viene creato come elemento figlio della finestra host. L'altezza e la larghezza di entrambe le finestre sono impostate sui valori passati dal costruttore, come illustrato in precedenza. Ciò garantisce che le dimensioni della finestra host e del controllo siano identiche all'area riservata nella pagina. Dopo aver creato le finestre, l'esempio restituisce un HandleRef oggetto che contiene il valore HWND della finestra 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

Implementare DestroyWindow e WndProc

Oltre a BuildWindowCore, è anche necessario eseguire l'override HwndHostdei WndProc metodi e DestroyWindowCore di . In questo esempio i messaggi per il controllo vengono gestiti dal MessageHook gestore, quindi l'implementazione di WndProc e DestroyWindowCore è minima. Nel caso di WndProc, impostare su handledfalse per indicare che il messaggio non è stato gestito e restituito 0. Per DestroyWindowCore, è sufficiente distruggere la finestra.

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

Ospitare il controllo nella pagina

Per ospitare il controllo nella pagina, creare prima di tutto una nuova istanza della ControlHost classe . Passare l'altezza e la larghezza dell'elemento bordo che contiene il controllo (ControlHostElement) al ControlHost costruttore. Questo garantisce che le dimensioni di ListBox siano corrette. Si ospita quindi il controllo nella pagina assegnando l'oggetto ControlHost alla Child proprietà dell'host Border.

L'esempio associa un gestore all'evento MessageHook di ControlHost per ricevere messaggi dal controllo . Questo evento viene generato per ogni messaggio inviato alla finestra ospitata. In questo caso, si tratta dei messaggi inviati alla finestra che esegue il wrapping del controllo ListBox effettivo, incluse le notifiche dal controllo. L'esempio chiama SendMessage per ottenere informazioni dal controllo e ne modifica il contenuto. I dettagli di come la pagina comunica con il controllo sono illustrati nella sezione successiva.

Nota

Si noti che esistono due dichiarazioni PInvoke per SendMessage. Ciò è necessario perché uno usa il wParam parametro per passare una stringa e l'altro lo usa per passare un numero intero. È necessaria una dichiarazione separata per ogni firma per assicurare che venga eseguito correttamente il marshalling dei dati.

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

Implementare la comunicazione tra il controllo e la pagina

È possibile modificare il controllo inviando i messaggi di Windows. Il controllo comunica quando l'utente interagisce con esso inviando notifiche alla propria finestra host. L'esempio Hosting di un controllo ListBox Win32 in WPF include un'interfaccia utente che fornisce diversi esempi di funzionamento:

  • Accodare un elemento all'elenco.

  • Eliminare l'elemento selezionato dall'elenco.

  • Visualizzare il testo dell'elemento attualmente selezionato.

  • Visualizzare il numero di elementi nell'elenco.

L'utente può anche selezionare un elemento nella casella di riepilogo facendo clic su di esso, proprio come per un'applicazione Win32 convenzionale. I dati visualizzati vengono aggiornati ogni vota che l'utente modifica lo stato della casella di riepilogo, selezionando, aggiungendo o accodando un elemento.

Per aggiungere elementi, inviare una casella di riepilogo a un LB_ADDSTRING messaggio. Per eliminare gli elementi, inviare LB_GETCURSEL per ottenere l'indice della selezione corrente e quindi LB_DELETESTRING per eliminare l'elemento. L'esempio invia LB_GETCOUNTanche e usa il valore restituito per aggiornare la visualizzazione che mostra il numero di elementi. Entrambe queste istanze di SendMessage usano una delle dichiarazioni PInvoke descritte nella sezione precedente.

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 l'utente seleziona un elemento o ne modifica la selezione, il controllo notifica alla finestra host inviando un WM_COMMAND messaggio che genera l'evento MessageHook per la pagina. Il gestore riceve le stesse informazioni della routine della finestra principale della finestra host. Passa inoltre un riferimento a un valore booleano, handled. handled Impostare su true per indicare che il messaggio è stato gestito e che non sono necessarie ulteriori elaborazioni.

WM_COMMAND viene inviato per diversi motivi, pertanto è necessario esaminare l'ID notifica per determinare se si tratta di un evento che si desidera gestire. L'ID è contenuto nella parola alta del wParam parametro . L'esempio usa operatori bit per bit per estrarre l'ID. Se l'utente ha effettuato o modificato la selezione, l'ID sarà LBN_SELCHANGE.

Quando LBN_SELCHANGE viene ricevuto, l'esempio ottiene l'indice dell'elemento selezionato inviando un LB_GETCURSEL messaggio al controllo . Per ottenere il testo, creare prima di tutto un oggetto StringBuilder. Si invia quindi il controllo a un LB_GETTEXT messaggio. Passare l'oggetto vuoto StringBuilder come wParam parametro. Quando SendMessage termina, l'oggetto StringBuilder conterrà il testo dell'elemento selezionato. Questo uso di SendMessage richiede un'altra dichiarazione PInvoke.

Impostare infine su handledtrue per indicare che il messaggio è stato gestito.

Vedi anche