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 explique comment utiliser une application, Exemple d’hébergement d’un contrôle ListBox Win32 dans WPF, qui intègre 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.

Exigences

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.

Note

Cette rubrique inclut un certain nombre d’exemples de code de l’exemple associé. Toutefois, pour une lisibilité, il n’inclut pas l’exemple de code complet. Vous pouvez obtenir ou afficher le code complet à partir de l’exemple 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 passent par les détails de chaque étape.

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 élément Border 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 membre BuildWindowCorede la classe HwndHost.

  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 via le paramètre hwndParent de la méthode BuildWindowCore. La fenêtre hébergée doit être créée en tant qu’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. Enveloppant les contrôles dans une fenêtre hôte permet à votre page WPF de recevoir des notifications des contrôles, en traitant certains problèmes Win32 particuliers liés aux notifications à travers la frontière HWND.

  6. Gérez les messages sélectionnés envoyés à la fenêtre hôte, tels que les notifications des contrôles enfants. Il existe deux façons de le faire.

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

    • Si vous préférez que WPF gère les messages, traitez l'événement de la classe HwndHostMessageHook 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 DestroyWindowCore et WndProc de HwndHost. Vous devez remplacer ces méthodes pour satisfaire le contrat HwndHost, 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 définissez-la comme 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 mise en 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 supérieur droit de la page a une zone carrée pour le contrôle ListBox hébergé.

Le code à implémenter cette disposition est assez simple. L’élément racine est un DockPanel qui a deux éléments fils. Le premier est un élément Border qui héberge le contrôle ListBox. Il occupe un carré de 200 x 200 dans le coin supérieur droit de la page. Le deuxième est un élément StackPanel 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 de l'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. Il 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

Remplacer 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. Étant donné que 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éé en tant qu’enfant de cette fenêtre.

La raison de cette approche est de simplifier le processus de réception des notifications du contrôle. La classe HwndHost 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 l’envoyer, mais vous ne recevez pas les notifications envoyées par le contrôle à 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 assignez le contrôle comme enfant de cette fenêtre. Cela vous permet de traiter les messages de la fenêtre hôte, y compris les notifications envoyées par le contrôle. Pour plus de commodité, étant donné que la fenêtre hôte est un peu plus qu’un simple emballage pour le contrôle, le package sera désigné sous le nom de contrôle ListBox.

Créer la fenêtre hôte et le 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, une approche beaucoup plus simple consiste à 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 des notifications du contrôle et nécessite un codage minimal.

Le HWND du contrôle est exposé via une propriété en lecture seule, de sorte que la page hôte puisse 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éé en tant qu’enfant de la fenêtre hôte. La hauteur et la largeur des deux fenêtres sont définies sur les valeurs passées au constructeur, décrites ci-dessus. Cela garantit que la taille de la fenêtre hôte et du contrôle est identique à la zone réservée de la page. Une fois les fenêtres créées, l’exemple retourne un objet HandleRef 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 WndProc et DestroyWindowCore des HwndHost. Dans cet exemple, les messages pour le contrôle sont gérés par le gestionnaire MessageHook, par conséquent, l’implémentation de WndProc et DestroyWindowCore est minimale. Dans le cas de WndProc, définissez handled sur false pour indiquer que le message n’a pas été géré et retourner zéro. Pour DestroyWindowCore, détruisez simplement 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 sur la page

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

L’exemple attache un gestionnaire à l’événement MessageHook du ControlHost pour recevoir 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 de la façon dont la page communique avec le contrôle sont abordés dans la section suivante.

Note

Notez qu’il existe deux déclarations PInvoke pour SendMessage. Cela est nécessaire, car l’un utilise le paramètre wParam 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 pour vous assurer 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 la communication entre le contrôle et la page

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

  • Ajoutez un élément à la liste.

  • Supprimer l’élément sélectionné de la liste

  • Affichez 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 modifie l’état de la boîte de liste en sélectionnant, en ajoutant ou en insérant un élément.

Pour ajouter des éléments, envoyez la zone de liste un message LB_ADDSTRING. Pour supprimer des éléments, envoyez LB_GETCURSEL pour obtenir l’index de la sélection actuelle, puis LB_DELETESTRING pour supprimer l’élément. L’exemple envoie également LB_GETCOUNTet utilise la valeur retournée pour mettre à jour l’affichage qui affiche le nombre d’éléments. Ces deux instances de SendMessage utilisent l’une des déclarations PInvoke décrites 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 message WM_COMMAND, 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 transmet également une référence à une valeur booléenne, handled. Vous définissez handled sur true pour 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 paramètre wParam. 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 est LBN_SELCHANGE.

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

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

Voir aussi