Partager via


Procédure pas à pas : hébergement d'un contrôle Win32 dans WPF

Windows Presentation Foundation (WPF) fournit un environnement riche pour créer des applications. Toutefois, lorsque vous vous appuyez massivement sur du code Win32, il peut être plus efficace de réutiliser au moins une partie de ce code dans votre application WPF, plutôt que de le réécrire entièrement. WPF fournit un mécanisme simple pour l'hébergement d'une fenêtre Win32 dans une page WPF.

Cette rubrique, Hébergement d'un contrôle ListBox Win32 dans Windows Presentation Foundation, exemple (page éventuellement en anglais), vous présente une application qui héberge un contrôle de zone de liste Win32. Cette procédure générale peut être étendue à l'hébergement de toute fenêtre Win32.

Cette rubrique comprend les sections suivantes.

  • Spécifications
  • La procédure de base :
  • Implémenter la Présentation Page
  • Implémenter une classe pour héberger le contrôle Microsoft Win32
  • Héberger le contrôle dans la page
  • Implémenter la communication entre le contrôle et la page
  • Rubriques connexes

Spécifications

Cette rubrique suppose que vous avez des connaissances de base en matière de programmation WPF et Win32. Pour une présentation générale de la programmation WPF, consultez Mise en route (WPF). Pour une présentation de la programmation Win32, consultez les divers manuels qui traitent de ce sujet, notamment La programmation Windows de Charles Petzold.

Comme l'exemple qui accompagne cette rubrique est implémenté dans C#, il utilise Platform Invocation Services (PInvoke) pour accéder à l'API Win32. Une certaine familiarisation avec PInvoke est utile, mais pas essentielle.

RemarqueRemarque

Cette rubrique inclut des exemples de code de l'exemple associé.Toutefois, il n'inclut pas l'exemple de code complet pour une meilleure lisibilité.Vous pouvez obtenir ou consulter le code complet de Hébergement d'un contrôle ListBox Win32 dans Windows Presentation Foundation, exemple.

La procédure de base :

Cette section décrit la procédure de base pour héberger une fenêtre Win32 sur une page WPF. Les sections restantes reprennent chaque étape en détail.

La procédure d'hébergement de base est :

  1. Implémenter 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émenter une classe pour héberger le contrôle qui hérite de HwndHost.

  3. Dans cette classe hôte, substituez le membre de classe BuildWindowCore HwndHost.

  4. Créer la fenêtre hébergée comme un 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 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 en général une fenêtre hôte comme un enfant du HWND et faites des contrôles les enfants de cette fenêtre d'hôte. L'encapsulation des contrôles dans une fenêtre hôte offre un moyen simple permettant à votre page WPF de recevoir des notifications des contrôles, qui traitent de quelques problèmes Win32 particuliers avec les notifications à travers la limite HWND.

  6. Gérer les messages sélectionnés envoyés à la fenêtre hôte, telles que les notifications des contrôles enfants. Il y a deux manières pour effectuer cette opération :

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

    • Si vous préférez que le WPF gère les messages, gérez la classe HwndHost d'événement 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 quand même substituer WndProc, mais vous n'avez besoin que d'une implémentation minime.

  7. Substituez les méthodes DestroyWindowCore et WndProc de HwndHost. Vous devez substituer ces méthodes pour satisfaire le contrat HwndHost, mais vous pouvez ne devoir fournir qu'une implémentation minime.

  8. Dans votre fichier code-behind, créez une instance de la classe d'hébergement de contrôle et faites-en un enfant de l'élément Border prévu pour héberger la fenêtre.

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

Implémenter la Présentation Page

La présentation pour 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 user interface (UI) vous permettant de manipuler le contrôle Win32. Le coin supérieur droit de la page comporte une zone carrée pour le contrôle ListBox hébergé.

Le code pour implémenter cette présentation est assez simple. L'élément racine est un DockPanel qui a deux éléments enfants. Le premier est un élément Border qui héberge le contrôle ListBox. Il occupe un carré de 200x200 dans le coin supérieur droit de la page. Le second est un élément StackPanel qui contient un jeu de contrôles WPF qui affichent des informations et vous permettent de manipuler le contrôle ListBox en définissant des propriétés d'interopérabilité exposées. Pour chacun des éléments qui sont enfants du StackPanel, consultez les documents de référence pour les divers éléments utilisés pour plus d'informations sur ce que sont ou font ces éléments, 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 n'a besoin d'aucun d'entre eux, ils sont fournis pour ajouter un peu d'interactivité à l'exemple).

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

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

Le cœur de cet exemple est la classe qui héberge effectivement 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 garantir que la taille du contrôle correspond à l'élément 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;
  }

Il y a également un jeu de constantes. Ces constantes proviennent pour l'essentiel de Winuser.h, et vous permettent d'utiliser des noms conventionnels lors de l'appel de fonctions 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;

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

Vous substituez cette méthode pour créer la fenêtre Win32 qui sera hébergée par la page et qui établira 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 hébergée effectivement par la page WPF. Le contrôle ListBox est créé comme un enfant de cette fenêtre.

La raison pour cette approche est de simplifier le processus de réception des notifications du contrôle. La classe HwndHost vous permet de traiter des messages envoyés à la fenêtre qu'elle héberge. Si vous hébergez un contrôle Win32 directement, vous recevez les messages envoyés à la boucle de messages 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 choses, que vous n'avez aucun moyen de détecter quand l'utilisateur interagit avec le contrôle. Créez à la place une fenêtre hôte et faites du contrôle un enfant de cette fenêtre. Cela vous permet de traiter les messages pour la fenêtre hôte y compris les notifications qui lui sont envoyées par le contrôle. Par souci de commodité, comme la fenêtre hôte n'est guère plus qu'un simple wrapper pour le contrôle, le package sera connu 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 enregistrant une classe de fenêtres, etc. Toutefois, une approche beaucoup plus simple consiste à créer une fenêtre avec la classe de fenêtres "static" prédéfinie. Cela vous fournit la procédure de fenêtre dont vous avez besoin pour recevoir des notifications du contrôle et requiert un codage minime.

Le HWND du contrôle est exposé via une propriété en lecture seule, afin que la page hôte puisse l'utiliser pour envoyer des messages au contrôle.

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

Le contrôle ListBox est créé comme un enfant de la fenêtre hôte. Les valeurs de hauteur et de largeur des deux fenêtres sont celles valeurs passées au constructeur, traité ci-dessus. Cela garantit que la taille de la fenêtre hôte et du contrôle est identique à la zone réservée sur la page. Après avoir créé les fenêtres, l'exemple retourne un objet HandleRef qui contient le HWND de la fenêtre hôte.

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

Implémenter DestroyWindow et WndProc

En plus de BuildWindowCore, vous devez substituer les méthodes WndProc et DestroyWindowCore du HwndHost. Dans cet exemple, les messages pour le contrôle sont gérés par le gestionnaire MessageHook, de sorte que l'implémentation de WndProc et DestroyWindowCore est minime. Dans le cas de WndProc, affectez à handled la valeur false pour indiquer que le message n'a pas été géré et retourner 0. Pour DestroyWindowCore, détruisez simplement la fenêtre.

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

Héberger le contrôle dans la page

Pour héberger le contrôle dans la page, vous créez tout d'abord une nouvelle 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 la Zone de liste est dimensionnée correctement. Vous hébergez ensuite le contrôle dans la page en assignant l'objet ControlHost à la propriété Child de l'hôte Border.

L'exemple joint 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, ce sont les messages envoyés à la fenêtre qui encapsulent 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 traités dans la section suivante.

RemarqueRemarque

Remarquez qu'il existe deux déclarations PInvoke pour SendMessage.Ceci est nécessaire parce que l'un utilise le paramètre wParam pour passer une chaîne et que l'autre l'utilise pour passer un entier.Vous avez besoin d'une déclaration séparée pour chaque signature afin de garantir que les données sont canalisées correctement.

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

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 notifie 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 Windows Presentation Foundation, exemplehttps://go.microsoft.com/fwlink/? l'exemple LinkID=159998 inclut une interface utilisateur qui fournit plusieurs exemples montrant comment effectuer les opérations suivantes :

  • Ajouter un élément à la liste.

  • Supprimer l'élément sélectionné dans 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 il le ferait pour une application Win32 conventionnelle. Les données affichées sont mises à jour chaque fois que l'utilisateur modifie l'état de la zone de liste en sélectionnant, en ajoutant, ou en rajoutant un élément.

Pour rajouter des éléments, envoyez un message LB_ADDSTRING à la zone de liste. 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_GETCOUNT et utilise la valeur retournée pour mettre à jour l'affichage qui montre le nombre d'éléments. Ces deux instances de SendMessage utilisent l'une des déclarations PInvoke traitées dans la section précédente.

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

Lorsque l'utilisateur sélectionne un élément ou modifie sa sélection, le contrôle notifie la fenêtre hôte en lui envoyant un message WM_COMMAND, 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 définissez handled à true pour indiquer que vous avez géré le message et qu'aucun traitement supplémentaire n'est requis.

WM_COMMAND est envoyé pour diverses raisons, de sorte que vous devez 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 de poids fort du paramètre wParam. Comme Microsoft .NET n'a pas de macro HIWORD, l'exemple utilise des opérateurs au niveau du bit pour extraire l'ID. Si l'utilisateur a fait ou modifié sa sélection, l'ID sera LBN_SELCHANGE.

Lorsque LBN_SELCHANGE est reçu, l'exemple récupère l'index de l'élément sélectionné en envoyant un message LB_GETCURSEL au contrôle. Pour obtenir le texte, vous devez d'abord créer un StringBuilder. Vous envoyez ensuite un message LB_GETTEXT au contrôle. Passez l'objet StringBuilder vide comme paramètre wParam. Lorsque SendMessage est retourné, le StringBuilder contiendra le texte de l'élément sélectionné. Cette utilisation de SendMessage requiert encore une autre déclaration PInvoke.

Pour finir, affectez à handled la valeur true pour indiquer que le message a été géré.

Voir aussi

Référence

HwndHost

Concepts

Interopérabilité WPF et Win32

Procédure pas à pas : mise en route de WPF