Condividi tramite


Procedura dettagliata: hosting di un controllo Win32 in WPF

Windows Presentation Foundation (WPF) fornisce un ambiente funzionale per la creazione di applicazioni. Tuttavia, quando si dispone di una grande quantità di codice Win32, può essere opportuno riutilizzare almeno parte di tale codice nell'applicazione WPF anziché riscriverlo completamente. WPF fornisce un meccanismo semplice per l'hosting di una finestra Win32 in una pagina WPF.

In questo argomento viene illustrata un'applicazione, Esempio di hosting di un controllo ListBox Win32 in WPF, che ospita un controllo casella di riepilogo Win32. Questa procedura generale può essere estesa all'hosting di qualsiasi finestra Win32.

Nel presente argomento sono contenute le seguenti sezioni.

  • Requisiti
  • Procedura di base
  • Implementare il layout della pagina
  • Implementare una classe per ospitare il controllo Microsoft Win32
  • Ospitare il controllo nella pagina
  • Implementare la comunicazione tra il controllo e la pagina
  • Argomenti correlati

Requisiti

Per questo argomento si presuppone una conoscenza di base della programmazione WPF e Win32. Per un'introduzione alla programmazione WPF, vedere Guida introduttiva (WPF). Per un'introduzione alla programmazione Win32, fare riferimento a uno dei numerosi manuali sull'argomento, in particolare Programming Windows di Charles Petzold.

Poiché l'esempio che accompagna questo argomento è implementato in C#, vengono utilizzati i Platform Invocation Services (PInvoke) per accedere all'API Win32. La conoscenza di PInvoke è utile ma non essenziale.

NotaNota

In questo argomento sono inclusi diversi esempi di codice relativi all'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 ListBox Win32 in WPF (la pagina potrebbe essere in inglese).

Procedura di base

In questa sezione viene illustrata la procedura di base per l'hosting di una finestra Win32 in una pagina WPF. Nelle sezioni restanti vengono illustrati i dettagli dei vari passaggi.

La procedura di hosting di base è la seguente:

  1. Implementare una pagina WPF per ospitare la finestra. A tal proposito è possibile creare un elemento Border per riservare una sezione della pagina per la finestra di hosting.

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

  3. In questa classe, eseguire l'override di BuildWindowCore del membro della classe HwndHost.

  4. Creare la finestra di hosting come figlio della finestra contenente la pagina WPF. Benché la programmazione WPF convenzionale non ne richieda l'uso esplicito, la pagina di hosting è una finestra con un handle (HWND). È possibile ricevere HWND della pagina tramite il parametro hwndParent del metodo BuildWindowCore. La finestra di hosting deve essere creata come figlio di HWND.

  5. Una volta creata la finestra host, restituire HWND della finestra di hosting. Per ospitare uno o più controlli Win32, si crea in genere una finestra host come figlio di HWND e, successivamente, i controlli vengono impostati come figli di tale finestra host. Il wrapping dei controlli in una finestra host è una procedura semplice che consente alla pagina WPF di ricevere notifiche dai controlli relative ad alcune problematiche Win32 particolari circa le notifiche attraverso il limite di HWND.

  6. Gestire i messaggi selezionati inviati alla finestra host, quali ad esempio le notifiche dai controlli figlio. A questo scopo, è possibile eseguire due operazioni.

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

    • Per fare in modo che WPF gestisca i messaggi, gestire l'evento MessageHook della classe HwndHost nel code-behind. Questo evento si verifica per ogni messaggio ricevuto dalla finestra di hosting. Scegliendo questa opzione, sarà comunque necessario eseguire l'override di WndProc, ma l'implementazione necessaria sarà minima.

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

  8. Nel file code-behind, creare un'istanza della classe di hosting dei controlli e impostarla come figlio dell'elemento Border nel quale dovrà essere ospitata la finestra.

  9. Comunicare con la finestra di hosting inviandole messaggi di Microsoft Windows e messaggi di gestione dalle finestre figlio, quali ad esempio le notifiche inviate dai controlli.

Implementare il layout della pagina

Il layout della pagina WPF che ospita il controllo ListBox è composto da due aree. Il lato sinistro della pagina ospita diversi controlli WPF che forniscono un'user interface (UI) tramite la quale è possibile modificare il controllo Win32. Nell'angolo superiore destro della pagina è presente un'area quadrata per il controllo ListBox ospitato.

Il codice di implementazione di questo layout è piuttosto semplice. L'elemento radice è un oggetto DockPanel che possiede due elementi figlio. Il primo è un elemento Border che ospita il controllo ListBox. Occupa un quadrato 200x200 nell'angolo superiore destro della pagina. Il secondo è un elemento StackPanel contenente un insieme di controlli WPF che consentono di visualizzare informazioni e modificare il controllo ListBox impostando proprietà di interoperatività esposte. Per ciascuno degli elementi figlio di StackPanel, vedere il materiale di riferimento dei vari elementi utilizzati per i dettagli sulla relativa definizione e l'utilizzo. Questi elementi vengono elencati nel codice di esempio che segue ma non illustrati nel dettaglio. Il modello di interoperatività di base non richiede alcuno di questi elementi, i quali vengono forniti al solo scopo di aggiungere interattività all'esempio.

<Window
  xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="https://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 ospitare il controllo Microsoft Win32

L'elemento principale di questo esempio è la classe che ospita realmente il controllo, ControlHost.cs. Questa classe eredita da HwndHost. Il costruttore accetta due parametri, altezza e larghezza, corrispondenti all'altezza e alla larghezza dell'elemento Border che ospita il controllo ListBox. Questi valori verranno utilizzati in un secondo momento per fare in modo che la dimensione del controllo corrisponda all'elemento Border.

  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
public class ControlHost : HwndHost
{
  IntPtr hwndControl;
  IntPtr hwndHost;
  int hostHeight, hostWidth;

  public ControlHost(double height, double width)
  {
    hostHeight = (int)height;
    hostWidth = (int)width;
  }

L'esempio include anche un insieme di costanti, in gran parte tratte da Winuser.h, che consentono di utilizzare nomi convenzionali per la chiamata alle funzioni Win32.

    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
internal const int
  WS_CHILD = 0x40000000,
  WS_VISIBLE = 0x10000000,
  LBS_NOTIFY = 0x00000001,
  HOST_ID = 0x00000002,
  LISTBOX_ID = 0x00000001,
  WS_VSCROLL = 0x00200000,
  WS_BORDER = 0x00800000;

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

Eseguire l'override di questo metodo per creare la finestra Win32 che verrà ospitata dalla pagina, quindi creare la connessione tra la finestra e la pagina. Poiché questo esempio implica l'hosting di un controllo ListBox, vengono create due finestre. La prima è la finestra realmente ospitata dalla pagina WPF. Il controllo ListBox viene creato come figlio di tale finestra.

Questo approccio viene utilizzato per semplificare il processo di ricezione delle notifiche dal controllo. La classe HwndHost consente di elaborare i messaggi inviati alla finestra di hosting. Se si ospita direttamente un controllo Win32, si ricevono i messaggi inviati al ciclo di messaggi interno del controllo. È possibile visualizzare e inviare messaggi al controllo, ma non si ricevono le notifiche che il controllo invia alla finestra padre. Ciò significa, tra le altre cose, che non si ha modo di rilevare quando l'utente interagisce con il controllo. Creare invece una finestra host e impostare il controllo come figlio di tale finestra. In questo modo sarà possibile elaborare i messaggi per la finestra host, incluse le notifiche inviate dal controllo. Per comodità, poiché la finestra host è poco più di un semplice wrapper per il controllo, il package verrà definito come controllo ListBox.

Creare la finestra host e il controllo ListBox

È possibile utilizzare PInvoke per creare una finestra host per il controllo mediante creazione e registrazione di una classe della finestra e così via. Tuttavia, un approccio molto più semplice consiste nella creazione di una finestra con la classe "statica" predefinita. Questo approccio fornisce la routine della finestra necessaria per ricevere le notifiche dal controllo e richiede codice minimo.

HWND del controllo viene esposto tramite una proprietà di sola lettura, in modo tale che la pagina host possa utilizzarlo per inviare messaggi al controllo.

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

Il controllo ListBox viene creato come figlio della finestra host. L'altezza e la larghezza di entrambe le finestre vengono impostate sui valori passati al costruttore, illustrati in precedenza. Così facendo le dimensioni della finestra host e del controllo saranno identiche all'area riservata nella pagina. Una volta create le finestre, nell'esempio viene restituito un oggetto HandleRef contenente HWND della finestra host.

    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
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);
}
    '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
//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);

Implementare DestroyWindow e WndProc

Oltre a BuildWindowCore, occorre anche eseguire l'override dei metodi WndProc e DestroyWindowCore di HwndHost. In questo esempio, i messaggi per il controllo vengono gestiti dal gestore MessageHook, pertanto l'implementazione di WndProc e DestroyWindowCore è minima. Nel caso di WndProc, impostare handled su false per indicare che il messaggio non è stato gestito e restituire 0. Per DestroyWindowCore, eliminare semplicemente la finestra.

    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
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);
}
    <DllImport("user32.dll", EntryPoint := "DestroyWindow", CharSet := CharSet.Unicode)>
    Friend Shared Function DestroyWindow(ByVal hwnd As IntPtr) As Boolean
    End Function
[DllImport("user32.dll", EntryPoint = "DestroyWindow", CharSet = CharSet.Unicode)]
internal static extern bool DestroyWindow(IntPtr hwnd);

Ospitare il controllo nella pagina

Per ospitare il controllo nella pagina, creare innanzitutto una nuova istanza della classe ControlHost. Passare l'altezza e la larghezza dell'elemento bordo contenente il controllo (ControlHostElement) al costruttore ControlHost. Così facendo ListBox avrà le dimensioni corrette. Ospitare quindi il controllo nella pagina assegnando l'oggetto ControlHost alla proprietà Child di Border host.

Nell'esempio viene associato un gestore all'evento MessageHook di ControlHost per ricevere messaggi dal controllo. Questo evento viene generato per ogni messaggio inviato alla finestra di hosting. In questo caso si tratta dei messaggi inviati alla finestra che esegue il wrapping del controllo ListBox effettivo, incluse le notifiche dal controllo. Nell'esempio viene chiamato SendMessage per ottenere informazioni dal controllo e modificarne il contenuto. I dettagli sulla comunicazione tra la pagina e il controllo verranno illustrati nella sezione successiva.

NotaNota

Notare che sono presenti due dichiarazioni PInvoke per SendMessage.Si tratta di una condizione necessaria in quanto una utilizza il parametro wParam per passare una stringa, mentre l'altra lo utilizza per passare un numero intero.È necessaria una dichiarazione separata per ciascuna firma affinché i dati vengano correttamente sottoposti a marshalling.

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
    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();
    }

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

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

Implementare la comunicazione tra il controllo e la pagina

Modificare il controllo inviandogli messaggi Windows. Il controllo avvisa quando l'utente interagisce con esso inviando notifiche alla finestra host. Nell'esempio di hosting di un controllo ListBox Win32 in WPF è inclusa un'interfaccia utente che fornisce vari esempi di procedure:

  • Aggiungere 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, esattamente come accade in un'applicazione Win32 convenzionale. I dati visualizzati vengono aggiornati ogni qualvolta l'utente modifica lo stato della casella di riepilogo selezionando o aggiungendo un elemento.

Per aggiungere elementi, inviare un messaggio LB_ADDSTRING alla casella di riepilogo. Per eliminare gli elementi, inviare LB_GETCURSEL per ottenere l'indice della selezione corrente, quindi LB_DELETESTRING per eliminare l'elemento. Nell'esempio viene anche inviato LB_GETCOUNT e viene utilizzato il valore restituito per aggiornare la visualizzazione del numero di elementi. Queste istanze di SendMessage utilizzano entrambe una delle dichiarazioni PInvoke illustrate nella sezione precedente.

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
private void AppendText(object sender, EventArgs args)
{
  if (txtAppend.Text != string.Empty)
  {
    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();
}

Quando l'utente seleziona un elemento o modifica la selezione, il controllo avvisa la finestra host inviandole un messaggio WM_COMMAND, il quale genera l'evento MessageHook per la pagina. Il gestore riceve le stesse informazioni della routine principale della finestra host. Passa inoltre un riferimento a un valore booleano, handled. Impostare handled su true per indicare che il messaggio è stato gestito e non necessita di ulteriore elaborazione.

WM_COMMAND viene inviato per diverse ragioni, pertanto è necessario esaminare l'identificatore della notifica per determinare se si tratti di un evento che si desidera gestire. L'identificatore è contenuto nel word alto del parametro wParam. Poiché Microsoft .NET non possiede una macro HIWORD, nell'esempio vengono utilizzati operatori bit per bit per estrarre l'identificatore. Se l'utente ha effettuato o modificato la selezione, l'identificatore sarà LBN_SELCHANGE.

Quando LBN_SELCHANGE viene ricevuto, nell'esempio si ottiene l'indice dell'elemento selezionato inviando un messaggio LB_GETCURSEL al controllo. Per ottenere il testo, creare innanzitutto un oggetto StringBuilder. Inviare quindi un messaggio LB_GETTEXT al controllo. Passare l'oggetto StringBuilder vuoto come parametro wParam. Quando SendMessage viene restituito, StringBuilder contiene il testo dell'elemento selezionato. Questo utilizzo di SendMessage richiede un'altra dichiarazione PInvoke.

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

Vedere anche

Riferimenti

HwndHost

Concetti

Interoperatività di WPF e Win32

Procedura dettagliata: introduzione a WPF