Freigeben über


Exemplarische Vorgehensweise: Hosten eines Win32-Steuerelements in WPF

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 erklärt Ihnen die Anwendung Ein Win32 ListBox-Steuerelement in einem WPF-Beispiel hosten, 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 Programming 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.

Anmerkung

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 den vollständigen Code aus dem Beispiel , in dem ein Win32-ListBox-Steuerelement im WPF gehostet wird, unterabrufen 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, um das Steuerelement zu hosten, das von HwndHosterbt.

  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 es bei der herkömmlichen WPF-Programmierung nicht erforderlich ist, sie explizit zu verwenden, handelt es sich bei der hostenden Seite 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, das als untergeordnetes Fenster des HWND fungiert, und machen die Steuerelemente zu untergeordneten Fenstern dieses Hostfensters. Das Einbetten der Steuerelemente in einem Hostfenster bietet eine einfache Möglichkeit für Ihre WPF-Seite, Benachrichtigungen von den Steuerelementen zu empfangen, was einige spezifische Probleme von Win32 mit Benachrichtigungen über die Grenze einer HWND hinweg behandelt.

  6. Behandeln Sie ausgewählte Nachrichten, die an das Hostfenster gesendet werden, z. B. Benachrichtigungen von untergeordneten Steuerelementen. Hierfür gibt es zwei Möglichkeiten.

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

    • Wenn Sie bevorzugen, dass WPF die Nachrichten verarbeitet, behandeln Sie das Ereignis der Klasse HwndHostMessageHook 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 zwar weiterhin WndProcaußer Kraft setzen, benötigen dazu 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 vertrag HwndHost zu erfüllen, sie müssen jedoch nur eine minimale Implementierung bereitstellen.

  8. Erstellen Sie in Ihrer Code-Behind-Datei eine Instanz der Steuerelement-Hostingklasse und machen Sie sie zu einem Kindelement des Border-Elements, das für das Hosting des Fensters vorgesehen ist.

  9. Kommunizieren Sie mit dem gehosteten Fenster, indem Sie ihm Microsoft Windows-Nachrichten senden und Nachrichten von seinen untergeordneten Fenstern verarbeiten, wie beispielsweise Benachrichtigungen, die von Steuerelementen gesendet werden.

Implementierung 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 mit zwei untergeordneten Elementen. Der erste ist ein Border-Element, das das ListBox-Steuerelement hostet. Es belegt ein 200 x 200 Pixel großes Feld 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 steuern, indem Sie die freigegebenen Interoperationseigenschaften festlegen. Für jedes der Elemente, die untergeordnete Elemente des StackPanelsind, siehe das Referenzmaterial, um Details zu erhalten, welche diese Elemente sind oder was sie tun. Diese sind im folgenden Beispielcode aufgeführt, werden hier jedoch nicht erläutert (das grundlegende Interaktionsmodell erfordert keine von ihnen, sie dienen lediglich 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 Sie eine Klasse zum Hosten des Microsoft Win32-Steuerelements.

Der Kern dieses Beispiels ist die Klasse, die tatsächlich das Steuerelement hostet, 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 hosten soll. Diese Werte werden später verwendet, um sicherzustellen, dass die Größe des Steuerelements dem Border Element entspricht.

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

Überschreiben Sie BuildWindowCore, um das Microsoft Win32 Window 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. Das erste ist das Fenster, das tatsächlich auf der WPF-Seite gehostet wird. Das ListBox-Steuerelement wird als untergeordnetes Element dieses Fensters erstellt.

Der Grund für diesen Ansatz besteht darin, den Prozess des Empfangens von Benachrichtigungen vom Steuerelement zu vereinfachen. Die HwndHost-Klasse ermöglicht es Ihnen, Nachrichten zu verarbeiten, die an das von ihr gehostete Fenster gesendet werden. 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 sein übergeordnetes 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. Zur Vereinfachung wird das Hostfenster, das kaum mehr als ein einfacher Wrapper für das Steuerelement ist, 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, was einen minimalen Programmieraufwand erfordert.

Der HWND des Steuerelements wird über eine schreibgeschützte Eigenschaft verfügbar gemacht, sodass die Hostseite ihn verwenden kann, um Nachrichten an das Steuerelement zu senden.

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 ist sichergestellt, dass die Größe des Hostfensters und des 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 BuildWindowCoremüssen Sie auch die methoden WndProc und DestroyWindowCore der HwndHostüberschreiben. In diesem Beispiel werden die Nachrichten 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 anzugeben, dass die Nachricht nicht behandelt wurde, und geben Sie 0 zurück. Für DestroyWindowCore, zerstören Sie einfach das Fenster.

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

Platzieren des Steuerelements auf der Seite

Um das Steuerelement auf der Seite zu hosten, erstellen Sie zunächst 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 Host-Borderzuweisen.

Im Beispiel wird ein Handler an das MessageHook-Ereignis der ControlHost angehängt, 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, welches das tatsächliche ListBox-Steuerelement umschließt, einschließlich Benachrichtigungen vom Steuerelement. 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.

Anmerkung

Beachten Sie, dass es zwei PInvoke-Deklarationen für SendMessage gibt. Dies ist erforderlich, da der parameter wParam verwendet wird, um eine Zeichenfolge zu übergeben, und der andere verwendet ihn, um eine ganze Zahl zu übergeben. Sie benötigen eine separate Erklärung für jede Signatur, um sicherzustellen, dass die Daten ordnungsgemäß zugewiesen 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

Die Kommunikation zwischen dem Steuerelement und der Seite implementieren

Sie bearbeiten das Steuerelement, indem Sie ihm Windows-Nachrichten senden. Das Steuerelement benachrichtigt Sie, wenn der Benutzer mit ihm interagiert, indem er Benachrichtigungen an das Hostfenster sendet. Das Hosten eines Win32 ListBox-Steuerelements in 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 angehängt wird.

Um Elemente anzufügen, senden Sie dem Listenfeld eine LB_ADDSTRING Nachricht. Um Elemente zu löschen, senden Sie LB_GETCURSEL, um den Index der aktuellen Auswahl abzurufen, und LB_DELETESTRING, um das Element zu löschen. Das Beispiel sendet auch LB_GETCOUNTund verwendet den zurückgegebenen Wert, um die Anzeige zu aktualisieren, die die Anzahl der Elemente anzeigt. Beide Instanzen von 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, indem es eine WM_COMMAND Nachrichtsendet, 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 handledübergeben. Sie legen handled auf true fest, um anzugeben, dass Sie die Nachricht verarbeitet haben und keine weitere Verarbeitung erforderlich ist.

WM_COMMAND wird aus verschiedenen Gründen gesendet, daher müssen Sie sich die Benachrichtigungs-ID ansehen, 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, wird die ID LBN_SELCHANGE.

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

Legen Sie abschließend handled auf true fest, um anzugeben, dass die Nachricht behandelt wurde.

Siehe auch