Partager via


Procédure pas à pas : héberger un contrôle Win32 dans WPF

Windows Presentation Foundation (WPF) fournit un environnement riche pour la création d’applications. Toutefois, lorsque vous avez un investissement important dans le code Win32, il peut être plus efficace de réutiliser au moins certains de ce code dans votre application WPF plutôt que de le réécrire complètement. WPF fournit un mécanisme simple pour l’hébergement d’une fenêtre Win32 sur une page WPF.

Cette rubrique vous guide tout au long d’une application, l’hébergement d’un contrôle ListBox Win32 dans l’exemple WPF, qui héberge un contrôle de zone de liste Win32. Cette procédure générale peut être étendue à l’hébergement de n’importe quelle fenêtre Win32.

Spécifications

Cette rubrique suppose une connaissance de base de la programmation des API WPF et Windows. Pour une présentation de base de la programmation WPF, consultez Prise en main. Pour une présentation de la programmation d’API Windows, consultez l’un des nombreux livres sur le sujet, en particulier Programmation Windows par Charles Petzold.

Étant donné que l’exemple qui accompagne cette rubrique est implémenté en C#, il utilise Platform Invocation Services (PInvoke) pour accéder à l’API Windows. Certaines connaissances de PInvoke sont utiles, mais pas essentielles.

Remarque

Cette rubrique comprend plusieurs exemples de code tirés de l'exemple associé. Cependant, pour une meilleure lecture, il n'inclut pas la totalité de l'exemple de code. Vous pouvez obtenir ou consulter le code complet dans l’Exemple d’hébergement d’un contrôle ListBox Win32 dans WPF.

Procédure de base

Cette section décrit la procédure de base pour l’hébergement d’une fenêtre Win32 sur une page WPF. Les sections restantes décrivent chaque étape en détails.

La procédure d’hébergement de base est la suivante :

  1. Implémentez une page WPF pour héberger la fenêtre. Une technique consiste à créer un Border élément pour réserver une section de la page pour la fenêtre hébergée.

  2. Implémentez une classe pour héberger le contrôle qui hérite de HwndHost.

  3. Dans cette classe, remplacez le HwndHost membre BuildWindowCorede classe .

  4. Créez la fenêtre hébergée en tant qu’enfant de la fenêtre qui contient la page WPF. Bien que la programmation WPF conventionnelle n’ait pas besoin de l’utiliser explicitement, la page d’hébergement est une fenêtre avec un handle (HWND). Vous recevez la page HWND par le biais du hwndParent paramètre de la BuildWindowCore méthode. La fenêtre hébergée doit être créée comme un enfant de ce HWND.

  5. Une fois que vous avez créé la fenêtre hôte, retournez le HWND de la fenêtre hébergée. Si vous souhaitez héberger un ou plusieurs contrôles Win32, vous créez généralement une fenêtre hôte en tant qu’enfant du HWND et rendez les contrôles enfants de cette fenêtre hôte. L’habillage des contrôles dans une fenêtre hôte permet à votre page WPF de recevoir des notifications des contrôles, qui traite de certains problèmes Win32 particuliers liés aux notifications sur la limite HWND.

  6. Gérez les messages sélectionnés envoyés à la fenêtre hôte, comme les notifications des contrôles enfants. Deux méthodes s’offrent à vous pour ce faire.

    • Si vous préférez gérer les messages dans votre classe d’hébergement, remplacez la WndProc méthode de la HwndHost classe.

    • Si vous préférez que WPF gère les messages, gérez l’événement HwndHost de classe MessageHook dans votre code-behind. Cet événement se produit pour chaque message reçu par la fenêtre hébergée. Si vous choisissez cette option, vous devez toujours remplacer WndProc, mais vous n’avez besoin que d’une implémentation minimale.

  7. Remplacez les méthodes et WndProc les DestroyWindowCore méthodes de HwndHost. Vous devez remplacer ces méthodes pour satisfaire le HwndHost contrat, mais vous devrez peut-être fournir une implémentation minimale.

  8. Dans votre fichier code-behind, créez une instance de la classe d’hébergement de contrôle et faites-le enfant de l’élément Border destiné à héberger la fenêtre.

  9. Communiquez avec la fenêtre hébergée en l’envoyant des messages Microsoft Windows et en gérant les messages à partir de ses fenêtres enfants, telles que les notifications envoyées par les contrôles.

Implémenter la disposition de la page

La disposition de la page WPF qui héberge le contrôle ListBox se compose de deux régions. Le côté gauche de la page héberge plusieurs contrôles WPF qui fournissent une interface utilisateur qui vous permet de manipuler le contrôle Win32. Le coin en haut à droite de la page contient une zone carrée pour le contrôle ListBox hébergé.

Le code permettant d’implémenter cette disposition est assez simple. L’élément racine est un DockPanel élément enfant qui a deux éléments enfants. Le premier est un Border élément qui héberge le contrôle ListBox. Il occupe un carré de 200 x 200 en haut à droite de la page. Le deuxième est un StackPanel élément qui contient un ensemble de contrôles WPF qui affichent des informations et vous permettent de manipuler le contrôle ListBox en définissant les propriétés d’interopérabilité exposées. Pour chacun des éléments qui sont des enfants du StackPanel, consultez le matériel de référence pour les différents éléments utilisés pour plus d’informations sur ce que ces éléments sont ou ce qu’ils font, ceux-ci sont répertoriés dans l’exemple de code ci-dessous, mais ne seront pas expliqués ici (le modèle d’interopérabilité de base ne nécessite aucun d’entre eux, ils sont fournis pour ajouter une interactivité à l’exemple).

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

Implémenter une classe pour héberger le contrôle Microsoft Win32

Le cœur de cet exemple est la classe qui héberge réellement le contrôle, ControlHost.cs. Hérite de HwndHost. Le constructeur prend deux paramètres, hauteur et largeur, qui correspondent à la hauteur et à la largeur de l’élément Border qui héberge le contrôle ListBox. Ces valeurs sont utilisées ultérieurement pour s’assurer que la taille du contrôle correspond à l’élément 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

Il existe également un ensemble de constantes. Ces constantes sont largement extraites de Winuser.h et vous permettent d’utiliser des noms conventionnels lors de l’appel de fonctions 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

Substituer BuildWindowCore pour créer la fenêtre Microsoft Win32

Vous remplacez cette méthode pour créer la fenêtre Win32 qui sera hébergée par la page et établir la connexion entre la fenêtre et la page. Comme cet exemple implique l’hébergement d’un contrôle ListBox, deux fenêtres sont créées. La première est la fenêtre qui est réellement hébergée par la page WPF. Le contrôle ListBox est créé comme un enfant de cette fenêtre.

Cette approche a pour but de simplifier le processus de réception des notifications du contrôle. La HwndHost classe vous permet de traiter les messages envoyés à la fenêtre qu’elle héberge. Si vous hébergez directement un contrôle Win32, vous recevez les messages envoyés à la boucle de message interne du contrôle. Vous pouvez afficher le contrôle et lui envoyer des messages, mais vous ne recevez pas les notifications que le contrôle envoie à sa fenêtre parente. Cela signifie, entre autres, que vous n’avez aucun moyen de détecter quand l’utilisateur interagit avec le contrôle. Au lieu de cela, créez une fenêtre hôte et définissez le contrôle comme un enfant de cette fenêtre. Cela vous permet de traiter les messages de la fenêtre hôte, y compris les notifications que le contrôle lui envoie. Pour des raisons pratiques, comme la fenêtre hôte n’est guère plus qu’un simple wrapper pour le contrôle, le package est un contrôle ListBox.

Créer la fenêtre hôte et un contrôle ListBox

Vous pouvez utiliser PInvoke pour créer une fenêtre hôte pour le contrôle en créant et en inscrivant une classe de fenêtre, et ainsi de suite. Toutefois, vous pouvez, beaucoup plus simplement, créer une fenêtre avec la classe de fenêtre « statique » prédéfinie. Cela vous fournit la procédure de fenêtre dont vous avez besoin pour recevoir les notifications du contrôle et nécessite un minimum de codage.

Le HWND du contrôle est exposé au moyen d’une propriété en lecture seule, de sorte que la page hôte peut l’utiliser pour envoyer des messages au contrôle.

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

Le contrôle ListBox est créé comme un enfant de la fenêtre hébergée. La hauteur et la largeur des deux fenêtres sont définies sur les valeurs passées au constructeur, comme décrit ci-dessus. Cela garantit que la taille de la fenêtre hôte et du contrôle est identique à la zone réservée dans la page. Une fois les fenêtres créées, l’exemple retourne un HandleRef objet qui contient le HWND de la fenêtre hôte.

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

Implémenter DestroyWindow et WndProc

En plus de BuildWindowCore, vous devez également remplacer les méthodes DestroyWindowCore et les WndProc méthodes du HwndHost. Dans cet exemple, les messages du contrôle sont gérés par le MessageHook gestionnaire, par conséquent l’implémentation WndProc et DestroyWindowCore est minimale. Dans le cas de WndProc, définissez handled pour false indiquer que le message n’a pas été géré et retourne 0. Pour DestroyWindowCore, il suffit de détruire la fenêtre.

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

Héberger le contrôle dans la page

Pour héberger le contrôle sur la page, vous créez d’abord une instance de la ControlHost classe. Passez la hauteur et la largeur de l’élément de bordure qui contient le contrôle (ControlHostElement) au ControlHost constructeur. Cela garantit que la taille du ListBox est correcte. Vous hébergez ensuite le contrôle sur la page en affectant l’objet ControlHost à la Child propriété de l’hôte Border.

L’exemple attache un gestionnaire à l’événement MessageHook de réception ControlHost des messages du contrôle. Cet événement est déclenché pour chaque message envoyé à la fenêtre hébergée. Dans ce cas, il s’agit des messages envoyés à la fenêtre qui encapsule le contrôle ListBox réel, y compris les notifications du contrôle. L’exemple appelle SendMessage pour obtenir des informations du contrôle et modifier son contenu. Les détails sur la façon dont la page communique avec le contrôle sont décrits dans la section suivante.

Remarque

Notez qu’il existe deux déclarations PInvoke pour SendMessage. Cela est nécessaire, car l’un utilise le wParam paramètre pour passer une chaîne et l’autre l’utilise pour passer un entier. Vous avez besoin d’une déclaration distincte pour chaque signature afin de garantir que les données sont correctement marshalées.

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

Implémenter une communication entre le contrôle et la page

Vous manipulez le contrôle en lui envoyant des messages Windows. Le contrôle vous notifie quand l’utilisateur interagit avec lui en envoyant des notifications à sa fenêtre hôte. L’exemple d’hébergement d’un contrôle ListBox Win32 dans WPF inclut une interface utilisateur qui fournit plusieurs exemples de fonctionnement :

  • Ajouter un élément à la liste.

  • Supprimez l'élément sélectionné de la liste.

  • Afficher le texte de l'élément actuellement sélectionné.

  • Afficher le nombre d’éléments de la liste.

L’utilisateur peut également sélectionner un élément dans la zone de liste en cliquant dessus, tout comme pour une application Win32 conventionnelle. Les données affichées sont mises à jour chaque fois que l’utilisateur change l’état de la zone de liste en sélectionnant ou ajoutant un élément.

Pour ajouter des éléments, envoyez la zone de liste un LB_ADDSTRING message. Pour supprimer des éléments, envoyez-les LB_GETCURSEL pour obtenir l’index de la sélection actuelle, puis LB_DELETESTRING pour supprimer l’élément. L’exemple envoie LB_GETCOUNTégalement et utilise la valeur retournée pour mettre à jour l’affichage qui affiche le nombre d’éléments. Ces deux instances d’utilisation de SendMessage l’une des déclarations PInvoke présentées dans la section précédente.

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

Lorsque l’utilisateur sélectionne un élément ou modifie sa sélection, le contrôle avertit la fenêtre hôte en l’envoyant un WM_COMMAND message, ce qui déclenche l’événement MessageHook pour la page. Le gestionnaire reçoit les mêmes informations que la procédure de fenêtre principale de la fenêtre hôte. Il passe également une référence à une valeur booléenne, handled. Vous devez handledtrue indiquer que vous avez géré le message et qu’aucun traitement supplémentaire n’est nécessaire.

WM_COMMAND est envoyé pour diverses raisons. Vous devez donc examiner l’ID de notification pour déterminer s’il s’agit d’un événement que vous souhaitez gérer. L’ID est contenu dans le mot élevé du wParam paramètre. L’exemple utilise des opérateurs au niveau du bit pour extraire l’ID. Si l’utilisateur a effectué ou modifié sa sélection, l’ID sera LBN_SELCHANGE.

Quand LBN_SELCHANGE il est reçu, l’exemple obtient l’index de l’élément sélectionné en envoyant le contrôle à un LB_GETCURSEL message. Pour obtenir le texte, vous créez d’abord un StringBuilder. Vous envoyez ensuite le contrôle un LB_GETTEXT message. Transmettez l’objet vide StringBuilder en tant que wParam paramètre. Lorsque SendMessage cette propriété est retournée, elle StringBuilder contient le texte de l’élément sélectionné. Cette utilisation nécessite SendMessage une autre déclaration PInvoke.

Enfin, définissez handled pour true indiquer que le message a été géré.

Voir aussi