Freigeben über


Anleitung: Win32-Steuerelement in WPF hosten

Windows Presentation Foundation (WPF) bietet eine umfangreiche Umgebung zum Erstellen von Anwendungen. Wenn Sie jedoch eine erhebliche Investition in Win32-Code haben, kann es effektiver sein, mindestens einen Teil dieses Codes in Ihrer WPF-Anwendung wiederzuverwenden, anstatt ihn vollständig neu zu schreiben. WPF bietet einen einfachen Mechanismus zum Hosten eines Win32-Fensters auf einer WPF-Seite.

Dieses Thema führt Sie durch die Anwendung, Hosting a Win32 ListBox Control in WPF Sample, die ein Win32-Listenfeld-Steuerelement hostet. Dieses allgemeine Verfahren kann auf das Hosten eines win32-Fensters erweitert werden.

Anforderungen

In diesem Thema wird eine grundlegende Vertrautheit sowohl mit der WPF- als auch der Windows-API-Programmierung vorausgesetzt. Eine grundlegende Einführung in die WPF-Programmierung finden Sie unter "Erste Schritte". Eine Einführung in die Programmierung von Windows-APIs finden Sie in einem der zahlreichen Bücher zum Thema, insbesondere in der Programmierung von Windows von Charles Petzold.

Da das Beispiel, das zu diesem Thema gehört, in C# implementiert wird, verwendet es Plattformaufrufdienste (PInvoke) für den Zugriff auf die Windows-API. Einige Kenntnisse mit PInvoke sind hilfreich, aber nicht unbedingt notwendig.

Hinweis

Dieses Thema enthält eine Reihe von Codebeispielen aus dem zugehörigen Beispiel. Aus Gründen der Lesbarkeit enthält sie jedoch nicht den vollständigen Beispielcode. Sie können vollständigen Code aus dem Hosten eines Win32 ListBox-Steuerelements im WPF-Beispiel abrufen oder anzeigen.

Grundlegendes Verfahren

In diesem Abschnitt wird das grundlegende Verfahren zum Hosten eines Win32-Fensters auf einer WPF-Seite beschrieben. Die restlichen Abschnitte durchlaufen die Details der einzelnen Schritte.

Das grundlegende Hostingverfahren lautet:

  1. Implementieren Sie eine WPF-Seite, um das Fenster zu hosten. Eine Technik besteht darin, ein Border Element zu erstellen, um einen Abschnitt der Seite für das gehostete Fenster zu reservieren.

  2. Implementieren Sie eine Klasse, die von HwndHost erbt, um das Steuerelement zu hosten.

  3. Überschreiben Sie in dieser Klasse das HwndHost Klassenelement BuildWindowCore.

  4. Erstellen Sie das gehostete Fenster als untergeordnetes Element des Fensters, das die WPF-Seite enthält. Obwohl bei der herkömmlichen WPF-Programmierung in der Regel keine explizite Verwendung erforderlich ist, handelt es sich bei der Hostseite um ein Fenster mit einem Handle (HWND). Sie erhalten die Seite HWND über den hwndParent Parameter der BuildWindowCore Methode. Das gehostete Fenster sollte als untergeordnetes Element dieses HWND erstellt werden.

  5. Nachdem Sie das Hostfenster erstellt haben, geben Sie den HWND des gehosteten Fensters zurück. Wenn Sie ein oder mehrere Win32-Steuerelemente hosten möchten, erstellen Sie in der Regel ein Hostfenster als untergeordnetes Element des HWND und machen die Steuerelemente untergeordnete Elemente dieses Hostfensters. Das Umschließen der Steuerelemente in einem Hostfenster bietet eine einfache Möglichkeit für Ihre WPF-Seite, Benachrichtigungen von den Steuerelementen zu erhalten, was einige spezielle Win32-Probleme mit Benachrichtigungen über die HWND-Grenze hinweg berücksichtigt.

  6. Behandeln Sie ausgewählte Meldungen, die an das Hostfenster gesendet werden, z. B. Benachrichtigungen von Kindsteuerungen. Es gibt zwei Möglichkeiten, dies zu tun.

    • Wenn Sie Nachrichten in Ihrer Hostingklasse lieber behandeln möchten, überschreiben Sie die WndProc Methode der HwndHost Klasse.

    • Wenn Sie die WPF-Nachrichten lieber verarbeiten möchten, behandeln Sie das HwndHost Klassenereignis MessageHook in Ihrem Code-Behind. Dieses Ereignis tritt für jede Nachricht auf, die vom gehosteten Fenster empfangen wird. Wenn Sie diese Option auswählen, müssen Sie WndProc weiterhin überschreiben, benötigen jedoch nur eine minimale Implementierung.

  7. Überschreiben Sie die Methoden DestroyWindowCore und WndProc von HwndHost. Sie müssen diese Methoden außer Kraft setzen, um den HwndHost Vertrag zu erfüllen, aber Sie müssen möglicherweise nur eine minimale Implementierung bereitstellen.

  8. Erstellen Sie in der Code-Behind-Datei eine Instanz der Steuerelement-hosting-Klasse, und machen Sie sie zum untergeordneten Element des Border Elements, das das Fenster hosten soll.

  9. Kommunizieren Sie mit dem gehosteten Fenster, indem Sie ihnen Microsoft Windows-Nachrichten senden und Nachrichten von ihren untergeordneten Fenstern verarbeiten, z. B. Benachrichtigungen, die von Steuerelementen gesendet werden.

Implementieren des Seitenlayouts

Das Layout für die WPF-Seite, auf der das ListBox-Steuerelement gehostet wird, besteht aus zwei Regionen. Auf der linken Seite der Seite werden mehrere WPF-Steuerelemente gehostet, die eine Benutzeroberfläche (UI) bereitstellen, mit der Sie das Win32-Steuerelement bearbeiten können. Die obere rechte Ecke der Seite weist einen quadratischen Bereich für das gehostete ListBox-Steuerelement auf.

Der Code zum Implementieren dieses Layouts ist ziemlich einfach. Das Stammelement ist ein DockPanel Element mit zwei untergeordneten Elementen. Der erste ist ein Border Element, das das ListBox-Steuerelement hosten soll. Es belegt ein Quadrat von 200x200 in der oberen rechten Ecke der Seite. Die zweite ist ein StackPanel Element, das eine Reihe von WPF-Steuerelementen enthält, die Informationen anzeigen und es Ihnen ermöglichen, das ListBox-Steuerelement zu bearbeiten, indem Sie verfügbar gemachte Interoperationseigenschaften festlegen. Alle Elemente, die Kinder des StackPanel sind, finden Sie im Referenzmaterial für die verschiedenen Elemente. Details darüber, was diese Elemente sind oder was sie tun, sind im folgenden Beispielcode aufgeführt, werden jedoch hier nicht erläutert. Das grundlegende Interoperationsmodell erfordert keine dieser Elemente; sie dienen dazu, dem Beispiel etwas Interaktivität hinzuzufügen.

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

Implementieren einer Klasse zum Hosten des Microsoft Win32-Steuerelements

Der Kern dieses Beispiels ist die Klasse, die das Steuerelement tatsächlich hosten, ControlHost.cs. Es erbt von HwndHost. Der Konstruktor akzeptiert zwei Parameter, Höhe und Breite, die der Höhe und Breite des Border Elements entsprechen, das das ListBox-Steuerelement hostt. Diese Werte werden später verwendet, um sicherzustellen, dass die Größe des Steuerelements mit dem Border Element übereinstimmt.

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

Es gibt auch eine Reihe von Konstanten. Diese Konstanten werden größtenteils von Winuser.h übernommen und ermöglichen es Ihnen, herkömmliche Namen beim Aufrufen von Win32-Funktionen zu verwenden.

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

BuildWindowCore überschreiben, um das Microsoft Win32-Fenster zu erstellen

Sie überschreiben diese Methode, um das Win32-Fenster zu erstellen, das von der Seite gehostet wird, und die Verbindung zwischen dem Fenster und der Seite herzustellen. Da in diesem Beispiel ein ListBox-Steuerelement gehostet wird, werden zwei Fenster erstellt. Der erste ist das Fenster, das tatsächlich von der WPF-Seite gehostet wird. Das ListBox-Steuerelement wird als Unterelement dieses Fensters erstellt.

Der Grund für diesen Ansatz besteht darin, den Prozess des Empfangens von Benachrichtigungen vom Steuerelement zu vereinfachen. Mit der HwndHost Klasse können Sie Nachrichten verarbeiten, die an das Fenster gesendet werden, das sie hosten soll. Wenn Sie ein Win32-Steuerelement direkt hosten, erhalten Sie die an die interne Nachrichtenschleife des Steuerelements gesendeten Nachrichten. Sie können das Steuerelement anzeigen und nachrichten senden, erhalten jedoch nicht die Benachrichtigungen, die das Steuerelement an das übergeordnete Fenster sendet. Dies bedeutet unter anderem, dass Sie keine Möglichkeit haben, zu erkennen, wann der Benutzer mit dem Steuerelement interagiert. Erstellen Sie stattdessen ein Hostfenster, und erstellen Sie das Steuerelement als untergeordnetes Element dieses Fensters. Auf diese Weise können Sie die Nachrichten für das Hostfenster verarbeiten, einschließlich der Benachrichtigungen, die vom Steuerelement an sie gesendet werden. Aus Gründen der Bequemlichkeit, da das Hostfenster kaum mehr als ein einfacher Wrapper für das Steuerelement ist, wird das Paket als ListBox-Steuerelement bezeichnet.

Erstellen des Hostfensters und des ListBox-Steuerelements

Sie können PInvoke verwenden, um ein Hostfenster für das Steuerelement zu erstellen, indem Sie eine Fensterklasse erstellen und registrieren usw. Ein viel einfacherer Ansatz besteht jedoch darin, ein Fenster mit der vordefinierten "statischen" Fensterklasse zu erstellen. Dadurch erhalten Sie die Fensterprozedur, die Sie benötigen, um Benachrichtigungen vom Steuerelement zu erhalten, und erfordert minimale Codierung.

Der HWND des Steuerelements wird über eine schreibgeschützte Eigenschaft verfügbar gemacht, sodass die Hostseite es zum Senden von Nachrichten an das Steuerelement verwenden kann.

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

Das ListBox-Steuerelement wird als untergeordnetes Element des Hostfensters erstellt. Die Höhe und Breite beider Fenster werden auf die Werte festgelegt, die an den Konstruktor übergeben werden, wie oben beschrieben. Dadurch wird sichergestellt, dass die Größe des Hostfensters und Steuerelements mit dem reservierten Bereich auf der Seite identisch ist. Nachdem die Fenster erstellt wurden, gibt das Beispiel ein HandleRef Objekt zurück, das den HWND des Hostfensters enthält.

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

Implementieren von DestroyWindow und WndProc

Zusätzlich zu BuildWindowCore müssen Sie auch die WndProc- und DestroyWindowCore-Methoden der HwndHost überschreiben. In diesem Beispiel werden die Meldungen für das Steuerelement vom MessageHook Handler behandelt, daher ist die Implementierung von WndProc und DestroyWindowCore minimal. Setzen Sie im Fall von WndProchandled auf false, um anzuzeigen, dass die Nachricht nicht behandelt wurde und geben Sie 0 zurück. Für DestroyWindowCore, einfach das Fenster zerstören.

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

Einbetten des Steuerelements auf der Seite

Um das Steuerelement auf der Seite zu hosten, erstellen Sie zuerst eine neue Instanz der ControlHost Klasse. Übergeben Sie die Höhe und Breite des Rahmenelements, das das Steuerelement (ControlHostElement) enthält, an den ControlHost Konstruktor. Dadurch wird sichergestellt, dass die Größe der ListBox korrekt ist. Anschließend hosten Sie das Steuerelement auf der Seite, indem Sie das ControlHost Objekt der Child Eigenschaft des Hosts Borderzuweisen.

Im Beispiel wird ein Handler an das MessageHook-Ereignis des ControlHost angefügt, um Nachrichten vom Steuerelement zu empfangen. Dieses Ereignis wird für jede Nachricht ausgelöst, die an das gehostete Fenster gesendet wird. In diesem Fall handelt es sich hierbei um die nachrichten, die an das Fenster gesendet werden, das das tatsächliche ListBox-Steuerelement umschließt, einschließlich Benachrichtigungen des Steuerelements. Im Beispiel wird SendMessage aufgerufen, um Informationen vom Steuerelement abzurufen und deren Inhalt zu ändern. Die Details dazu, wie die Seite mit dem Steuerelement kommuniziert, werden im nächsten Abschnitt erläutert.

Hinweis

Beachten Sie, dass es zwei PInvoke-Deklarationen für SendMessage gibt. Dies ist erforderlich, da ein wParam Parameter verwendet, um eine Zeichenfolge zu übergeben, und der andere verwendet ihn, um eine ganze Zahl zu übergeben. Sie benötigen eine separate Deklaration für jede Signatur, damit sichergestellt wird, dass die Daten ordnungsgemäß übermittelt werden.

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

Implementierung der Kommunikation zwischen dem Steuerelement und der Seite

Sie manipulieren das Steuerelement, indem Sie Windows-Nachrichten an es senden. Das Steuerelement benachrichtigt Sie, wenn der Benutzer mit ihm interagiert, indem er Benachrichtigungen an das Hostfenster sendet. Das Hosten eines Win32 ListBox-Steuerelements im WPF-Beispiel enthält eine Benutzeroberfläche, die mehrere Beispiele für die Funktionsweise bietet:

  • Fügen Sie ein Element an die Liste an.

  • Löschen des ausgewählten Elements aus der Liste

  • Zeigt den Text des aktuell ausgewählten Elements an.

  • Zeigt die Anzahl der Elemente in der Liste an.

Der Benutzer kann auch ein Element im Listenfeld auswählen, indem er darauf klickt, genau wie bei einer herkömmlichen Win32-Anwendung. Die angezeigten Daten werden jedes Mal aktualisiert, wenn der Benutzer den Status des Listenfelds ändert, indem ein Element ausgewählt, hinzugefügt oder angefügt wird.

Um Elemente anzufügen, senden Sie dem Listenfeld eine LB_ADDSTRING Nachricht. Zum Löschen von Elementen senden Sie LB_GETCURSEL, um den Index der aktuellen Auswahl abzurufen, und dann LB_DELETESTRING, um das Element zu löschen. Das Beispiel sendet LB_GETCOUNTaußerdem und verwendet den zurückgegebenen Wert, um die Anzeige zu aktualisieren, die die Anzahl der Elemente anzeigt. Beide Instanzen der SendMessage verwenden eine der im vorherigen Abschnitt erläuterten PInvoke-Deklarationen.

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

Wenn der Benutzer ein Element auswählt oder seine Auswahl ändert, benachrichtigt das Steuerelement das Hostfenster durch Senden einer WM_COMMAND Nachricht, wodurch das MessageHook Ereignis für die Seite ausgelöst wird. Der Handler empfängt die gleichen Informationen wie die Hauptfensterprozedur des Hostfensters. Außerdem wird ein Verweis auf einen booleschen Wert übergeben. handled Sie legen handledtrue fest, dass Sie die Nachricht verarbeitet haben und keine weitere Verarbeitung erforderlich ist.

WM_COMMAND wird aus verschiedenen Gründen gesendet, sodass Sie die Benachrichtigungs-ID überprüfen müssen, um festzustellen, ob es sich um ein Ereignis handelt, das Sie behandeln möchten. Die ID ist im hohen Wort des wParam Parameters enthalten. Im Beispiel werden bitweise Operatoren verwendet, um die ID zu extrahieren. Wenn der Benutzer seine Auswahl vorgenommen oder geändert hat, lautet LBN_SELCHANGEdie ID .

Wenn LBN_SELCHANGE empfangen wird, erhält das Beispiel den Index des ausgewählten Elements, indem es dem Steuerelement eine LB_GETCURSEL Nachricht sendet. Um den Text abzurufen, erstellen Sie zuerst eine StringBuilder. Anschließend senden Sie dem Steuerelement eine LB_GETTEXT Nachricht. Übergeben Sie das leere StringBuilder Objekt als wParam Parameter. Wenn SendMessage zurückkehrt, enthält StringBuilder den Text des ausgewählten Elements. Für diese Verwendung SendMessage ist eine weitere PInvoke-Deklaration erforderlich.

Abschließend legen Sie handled auf true fest, um anzuzeigen, dass die Nachricht behandelt wurde.

Siehe auch