Freigeben über


Exemplarische Vorgehensweise: Hosten eines Win32-Steuerelements in WPF

Windows Presentation Foundation (WPF) stellt eine umfangreiche Umgebung zum Erstellen von Anwendungen bereit. Wenn Sie allerdings bereits erheblichen Aufwand für Win32-Code betrieben haben, kann es effektiver sein, zumindest einen Teil dieses Codes in Ihrer WPF-Anwendung wieder zu verwenden, anstatt ihn vollständig neu zu schreiben. WPF bietet ein einfaches Verfahren zum Hosten eines Win32-Fensters auf einer WPF-Seite.

Dieses Thema führt Sie durch eine Anwendung, Beispiel für das Hosten eines Win32-Listenfeld-Steuerelements in Windows Presentation Foundation, die ein Win32-Listenfeldsteuerelement hostet. Dieses allgemeine Verfahren kann für das Hosten beliebiger Win32-Fenster erweitert werden.

Dieses Thema enthält folgende Abschnitte.

  • Voraussetzungen
  • Das grundlegende Verfahren
  • Implementieren des Seitenlayouts
  • Implementieren einer Klasse zum Hosten des Microsoft Win32-Steuerelements
  • Hosten des Steuerelements auf der Seite
  • Implementieren der Kommunikation zwischen dem Steuerelement und der Seite
  • Verwandte Abschnitte

Voraussetzungen

In diesem Thema wird davon ausgegangen, dass Sie mit den Grundlagen der WPF-Programmierung und Win32-Programmierung vertraut sind. Eine Einführung in die Grundlagen der WPF-Programmierung finden Sie unter Erste Schritte (WPF). Eine Einführung in die Win32-Programmierung finden Sie in zahlreichen Büchern zu diesem Thema, insbesondere in Windows-Programmierung von Charles Petzold.

Da das Beispiel für dieses Thema in C# implementiert wurde, werden die Platform Invocation Services (PInvoke) für den Zugriff auf die Win32-API verwendet. Grundkenntnisse in PInvoke sind hilfreich, aber keine Voraussetzung.

HinweisHinweis

Dieses Thema enthält eine Reihe von Codebeispielen aus dem genannten Beispiel.Aus Gründen der besseren Lesbarkeit wird jedoch nicht der gesamte Beispielcode aufgeführt.Den vollständigen Code können Sie unter Beispiel für das Hosten eines Win32-Listenfeld-Steuerelements in Windows Presentation Foundation abrufen oder dort einsehen.

Das grundlegende Verfahren

Dieser Abschnitt umfasst das grundlegende Verfahren zum Hosten eines Win32-Fensters auf einer WPF-Seite. In den weiteren Abschnitten werden die einzelnen Schritte näher erläutert.

Das grundlegende Hostingverfahren verläuft wie folgt:

  1. Implementieren Sie eine WPF-Seite, um das Fenster zu hosten. Eine Möglichkeit ist das Erstellen eines Border-Elements, um einen Abschnitt der Seite für das gehostete Fenster zu reservieren.

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

  3. Überschreiben Sie in dieser Klasse den HwndHost-Klassenmember 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 explizit verwendet werden muss, stellt die Hostingseite ein Fenster mit einem Handle (HWND) dar. Sie erhalten das HWND der Seite durch den hwndParent-Parameter der BuildWindowCore-Methode. Das gehostete Fenster sollte als untergeordnetes Element von diesem HWND erstellt werden.

  5. Sobald Sie das Hostfenster erstellt haben, geben Sie das 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 zu untergeordneten Elementen dieses Hostfensters. Durch Umschließen der Steuerelemente mit einem Hostfenster kann die WPF-Seite Benachrichtigungen von den Steuerelementen erhalten. Dabei geht es um bestimmte Probleme von Win32 im Zusammenhang mit Benachrichtigungen über die HWND-Grenze hinweg.

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

    • Wenn Sie Meldungen bevorzugt in der Hostingklasse verarbeiten, überschreiben Sie die WndProc-Methode der HwndHost-Klasse.

    • Sollen die Meldungen von WPF verarbeitet werden, verarbeiten Sie das MessageHook-Ereignis der HwndHost-Klasse im Code-Behind. Dieses Ereignis tritt für jede Meldung auf, die vom gehosteten Fenster empfangen wird. Wenn Sie diese Option auswählen, müssen Sie WndProc zwar immer noch überschreiben, benötigen jedoch nur eine minimale Implementierung.

  7. Überschreiben Sie die DestroyWindowCore-Methode und WndProc-Methode von HwndHost. Sie müssen diese Methoden überschreiben, um dem HwndHost-Vertrag zu entsprechen, aber müssen u. U. nur eine minimale Implementierung bereitstellen.

  8. Erstellen Sie in der Code-Behind-Datei eine Instanz der Hostingklasse des Steuerelements, und machen Sie diese zu einem untergeordneten Element des Border-Elements, das das Fenster hosten soll.

  9. Kommunizieren Sie mit dem gehosteten Fenster, indem Sie Microsoft Windows-Meldungen dorthin senden und Meldungen der untergeordneten Fenster verarbeiten, z. B. Benachrichtigungen von Steuerelementen.

Implementieren des Seitenlayouts

Das Layout für die WPF-Seite, die das ListBox-Steuerelement hostet, besteht aus zwei Bereichen. Auf der linken Seite werden verschiedene WPF-Steuerelemente gehostet, die eine user interface (UI) bereitstellen, mit der Sie das Win32-Steuerelement bearbeiten können. In der rechten oberen Ecke der Seite befindet sich ein quadratischer Bereich für das gehostete ListBox-Steuerelement.

Der Code zum Implementieren dieses Layouts ist ganz einfach. Das Stammelement entspricht DockPanel mit zwei untergeordneten Elementen. Das erste ist ein Border-Element, das das ListBox-Steuerelement hostet. Es belegt einen quadratischen Bereich von 200x200 in der rechten oberen Ecke der Seite. Das zweite ist ein StackPanel-Element mit einer Gruppe von WPF-Steuerelementen, die Informationen anzeigen und Ihnen Änderungen am ListBox-Steuerelement ermöglichen, indem verfügbar gemachte Interoperationseigenschaften festgelegt werden. Die einzelnen Elemente, die untergeordnete Elemente von StackPanel darstellen, werden im Referenzmaterial für die verschiedenen Elemente näher erläutert. Dort wird erklärt, worum es sich bei den Elementen handelt und was sie bewirken. Sie sind im Beispielcode unten aufgelistet, werden aber an dieser Stelle nicht erklärt (sie sind nicht für das grundlegende Interoperationsmodell erforderlich, sondern werden bereitgestellt, um Interaktivität im Beispiel zu ermöglichen).

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

Implementieren einer Klasse zum Hosten des Microsoft Win32-Steuerelements

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

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

Außerdem ist ein Satz von Konstanten verfügbar. Die Konstanten stammen zum großen Teil aus Winuser.h und ermöglichen die Verwendung konventioneller Namen beim Aufrufen von Win32-Funktionen.

    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;

Überschreiben von BuildWindowCore zum Erstellen des Microsoft Win32-Fensters

Sie überschreiben diese Methode zum Erstellen des von der Seite gehosteten Win32-Fensters und stellen die Verbindung zwischen dem Fenster und der Seite her. Da in diesem Beispiel auch ein ListBox-Steuerelement gehostet wird, werden zwei Fenster erstellt. Das erste ist das tatsächlich von der WPF-Seite gehostete Fenster. Das ListBox-Steuerelement wird als untergeordnetes Element dieses Fensters erstellt.

Mit diesem Ansatz soll der Prozess des Empfangens von Benachrichtigungen vom Steuerelement vereinfacht werden. Mit der HwndHost-Klasse können Sie Meldungen verarbeiten, die an das Fenster gesendet wurden, in dem sie gehostet wird. Wenn Sie ein Win32-Steuerelement direkt hosten, empfangen Sie die Meldungen, die an die interne Nachrichtenschleife des Steuerelements gesendet wurden. Sie können das Steuerelement anzeigen und Meldungen daran senden, aber Sie empfangen nicht die Benachrichtigungen, die vom Steuerelement an das übergeordnete Fenster gesendet werden. Das bedeutet u. a., dass Sie nicht erkennen können, wann der Benutzer mit dem Steuerelement interagiert. Erstellen Sie stattdessen ein Hostfenster, und machen Sie das Steuerelement zu einem untergeordneten Element dieses Fensters. Dadurch können Sie die Meldungen für das Hostfenster verarbeiten, darunter die Benachrichtigungen, die vom Steuerelement dorthin gesendet wurden. Der Einfachheit halber wird das Paket als ListBox-Steuerelement bezeichnet, da das Hostfenster im Grunde nur einen einfachen Wrapper für das Steuerelement darstellt.

Erstellen des Hostfensters und des ListBox-Steuerelements

Sie können mit PInvoke ein Hostfenster für das Steuerelement erstellen, indem Sie eine Fensterklasse erstellen und registrieren usw. Einfacher ist es jedoch, ein Fenster mit der vordefinierten "statischen" Fensterklasse zu erstellen. Dadurch verfügen Sie über die Fensterprozedur, die Sie zum Empfangen von Benachrichtigungen vom Steuerelement benötigen, und der Codierungsaufwand ist minimal.

Das HWND des Steuerelements wird durch eine schreibgeschützte Eigenschaft verfügbar gemacht, sodass die Hostseite Meldungen an das Steuerelement senden kann.

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

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

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

Implementieren von DestroyWindow und WndProc

Zusätzlich zu BuildWindowCore müssen Sie auch die WndProc-Methode und DestroyWindowCore-Methode von HwndHost überschreiben. In diesem Beispiel werden die Meldungen für das Steuerelement vom MessageHook-Handler verarbeitet, daher ist die Implementierung von WndProc und DestroyWindowCore minimal. Im Fall von WndProc legen Sie handled auf false fest, um anzugeben, dass die Meldung nicht verarbeitet wurde und geben 0 (null) zurück. Für DestroyWindowCore löschen Sie einfach das Fenster.

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

Hosten 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 die Breite des Rahmenelements, das das Steuerelement (ControlHostElement) enthält, an den ControlHost-Konstruktor. Dadurch ist sichergestellt, dass das Listenfeld die richtige Größe hat. Anschließend hosten Sie das Steuerelement auf der Seite, indem Sie das ControlHost-Objekt der Child-Eigenschaft von Border des Hosts zuweisen.

Im Beispiel wird ein Handler an das MessageHook-Ereignis von ControlHost angefügt, um Meldungen vom Steuerelement zu empfangen. Dieses Ereignis wird für jede Meldung ausgelöst, die an das gehostete Fenster gesendet wird. In diesem Fall handelt es sich dabei um die Meldungen, die an das Fenster gesendet wurden, das das tatsächliche ListBox-Steuerelement umschließt, darunter Benachrichtigungen des Steuerelements. Im Beispiel wird SendMessage aufgerufen, um Informationen aus dem Steuerelement abzurufen und dessen Inhalt zu ändern. Im nächsten Abschnitt wird die Kommunikation zwischen Seite und Steuerelement ausführlicher erläutert.

HinweisHinweis

Beachten Sie, dass es zwei PInvoke-Deklarationen für SendMessage gibt.Das ist erforderlich, weil der wParam-Parameter von einer Deklaration zum Übergeben einer Zeichenfolge und von der anderen zum Übergeben einer ganzen Zahl verwendet wird.Sie benötigen für jede Signatur eine separate Deklaration, um sicherzustellen, dass die Daten ordnungsgemäß gemarshallt werden.

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

Implementieren der Kommunikation zwischen dem Steuerelement und der Seite

Sie bearbeiten das Steuerelement, indem Sie Windows-Meldungen an das Steuerelement senden. Sie werden vom Steuerelement benachrichtigt, wenn der Benutzer damit interagiert, indem es Benachrichtigungen an dessen Hostfenster sendet. Das Beispiel für das Hosten eines Win32-Listenfeld-Steuerelements in Windows Presentation Foundation umfasst eine Benutzeroberfläche, mit der folgende Funktionen veranschaulicht werden:

  • Anfügen eines Elements an die Liste

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

  • Anzeigen des Texts des zurzeit ausgewählten Elements

  • Anzeigen der Anzahl der Elemente in der Liste

Der Benutzer kann auch ein Element im Listenfeld auswählen, indem er wie bei einer normalen Win32-Anwendung darauf klickt. Die angezeigten Daten werden jedes Mal aktualisiert, wenn der Benutzer den Status des Listenfelds durch Auswählen, Hinzufügen oder Anfügen eines Elements ändert.

Um Elemente anzufügen, senden Sie eine LB_ADDSTRING-Meldung an das Listenfeld. Wenn Sie Elemente löschen möchten, senden Sie LB_GETCURSEL, um den Index der aktuellen Auswahl abzurufen, und dann LB_DELETESTRING, um das Element zu löschen. In dem Beispiel wird außerdem LB_GETCOUNT gesendet und der zurückgegebene Wert verwendet, um die Anzeige mit der Anzahl der Elemente zu aktualisieren. Diese beiden Instanzen von SendMessage verwenden eine der PInvoke-Deklarationen, die im vorherigen Abschnitt erläutert wurden.

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

Wenn der Benutzer ein Element auswählt oder die Auswahl ändert, benachrichtigt das Steuerelement das Hostfenster durch Senden einer WM_COMMAND-Meldung, die das MessageHook-Ereignis für die Seite auslöst. Der Handler empfängt dieselben Informationen wie die Hauptfensterprozedur des Hostfensters. Außerdem wird ein Verweis an einen booleschen Wert übergeben: handled. Sie legen handled auf true fest, um anzugeben, dass die Meldung verarbeitet wurde und keine weitere Verarbeitung erforderlich ist.

Da WM_COMMAND aus den unterschiedlichsten Gründen gesendet wird, müssen Sie die Benachrichtigungs-ID daraufhin überprüfen, ob ein Ereignis behandelt werden soll. Die ID ist im hohen WORD des wParam-Parameters enthalten. Da Microsoft .NET kein HIWORD-Makro aufweist, werden im Beispiel zum Extrahieren der ID bitweise Operatoren verwendet. Wenn der Benutzer die Auswahl vorgenommen oder geändert hat, lautet die ID LBN_SELCHANGE.

Wird LBN_SELCHANGE empfangen, wird im Beispiel der Index des ausgewählten Elements abgerufen. Dies geschieht durch Senden einer LB_GETCURSEL-Meldung an das Steuerelement. Zum Abrufen des Texts erstellen Sie zuerst StringBuilder. Anschließend senden Sie eine LB_GETTEXT-Meldung an das Steuerelement. Übergeben Sie das leere StringBuilder-Objekt als wParam-Parameter. Wenn SendMessage zurückgegeben wird, enthält StringBuilder den Text des ausgewählten Elements. Diese Verwendung von SendMessage erfordert eine weitere PInvoke-Deklaration.

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

Siehe auch

Referenz

HwndHost

Konzepte

Interaktion zwischen WPF und Win32

Exemplarische Vorgehensweise: Erste Schritte mit WPF