Vorgehensweise: Hosten eines Win32-Steuerelements in WPF
Windows Presentation Foundation (WPF) bietet eine umfangreiche Umgebung zum Erstellen von Anwendungen. 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 in einer WPF-Seite.
Dieses Thema führt Sie durch eine Anwendung, Beispiel zum Hosten eines Win32-ListBox-Steuerelements in WPF, die ein Win32-Listenfeld-Steuerelement hostet. Dieses allgemeine Verfahren kann für das Hosten beliebiger Win32-Fenster erweitert werden.
Anforderungen
In diesem Tutorial wird vorausgesetzt, dass Sie mit den Grundlagen der WPF-und Windows API-Programmierung vertraut sind. Eine Einführung in die Grundlagen der WPF-Programmierung finden Sie unter Erste Schritte. Eine Einführung in die Windows API-Programmierung finden Sie in zahlreichen Büchern zu diesem Thema, insbesondere in Programming Windows von Charles Petzold.
Da das in diesem Thema verwendete Beispiel in C# implementiert ist, verwendet es Platform Invocation Services (PInvoke) für den Zugriff auf die Windows API. Grundkenntnisse in PInvoke sind hilfreich, aber keine Voraussetzung.
Hinweis
Dieses Lernprogramm 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 zum Hosten eines Win32-ListBox-Steuerelements in WPF ansehen oder von dort kopieren.
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.
Die grundlegende Vorgehensweise beim Hosten ist:
Implementieren einer WPF-Seite, die das Fenster hostet. Eine Möglichkeit ist das Erstellen eines Border-Elements, um einen Abschnitt der Seite für das zu hostende Fenster zu reservieren.
Implementieren Sie eine Klasse, um das Steuerelement zu hosten, das von HwndHost erbt.
Überschreiben Sie in dieser Klasse den Member BuildWindowCore der Klasse HwndHost.
Erstellen Sie das gehostete Fenster als untergeordnetes Element des Fensters, das die WPF-Seite enthält. Obwohl in herkömmlicher WPF-Programmierung eine solch explizite Verwendung nicht erforderlich ist, ist die Hostseite ein Fenster mit einem Handle (HWND). Sie erhalten das HWND der Seite über die
hwndParent
-Parameter der BuildWindowCore-Methode. Das gehostete Fenster sollte als untergeordnetes Element dieses HWND erstellt werden.Sobald Sie das Hostfenster erstellt haben, geben Sie das HWND des gehosteten Fensters zurück. Wenn Sie eine 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. Das Einbinden von Steuerelementen in ein Hostfenster bietet eine einfache Möglichkeit, Benachrichtigungen der Steuerelemente an Ihre WPF-Seite zu übergeben und zugleich einige besondere Schwierigkeiten in Win32 bezüglich Benachrichtigungen über HWND-Grenzen hinweg zu umgehen.
Behandeln Sie spezifische an das Hostfenster gesendete Nachrichten, z.B. Benachrichtigungen von untergeordneten Steuerelementen. Es gibt hierbei zwei Möglichkeiten.
Wenn Sie es bevorzugen, Nachrichten in ihrer Hostklasse zu behandeln, überschreiben Sie die WndProc-Methode der HwndHost-Klasse.
Wenn bevorzugen, dass WPF die Nachrichten handhabt, verwenden Sie das MessageHook-Ereignis der HwndHost-Klasse in ihrem CodeBehind. Dieses Ereignis tritt für jede Nachricht auf, die vom gehosteten Fenster empfangen wird. Wenn Sie diese Option auswählen, müssen Sie immer noch WndProc überschreiben, aber Sie benötigen nur eine minimale Implementierung.
Überschreiben Sie die DestroyWindowCore-Methoden und WndProc-Methoden von HwndHost. Sie müssen diese Methoden überschreiben, um den HwndHost-Vertrag zu erfüllen, allerdings genügt eine minimale Implementierung.
Erstellen Sie in der CodeBehind-Datei eine Instanz der Hostingklasse des Steuerelements, und machen Sie diese zu einem untergeordneten Element des Border-Elements, welches das Fenster hosten soll.
Sie können mit dem gehosteten Fenster kommunizieren, indem Sie ihm Microsoft Windows-Nachrichten senden und aus dessen untergeordneten Fenstern gesendete Nachrichten, z.B. von Steuerelementen gesendete Benachrichtigungen, behandeln.
Implementieren des Seitenlayouts
Das Layout für die WPF-Seite, die das ListBox-Steuerelement hostet, besteht aus zwei Bereichen. Die linke Seite der Seite hostet mehrere WPF-Steuerelemente, die eine Benutzeroberfläche bereitstellen, die Ihnen gestattet, das Win32-Steuerelement anzupassen. In der oberen rechten Ecke der Seite befindet sich ein quadratischer Bereich für das gehostete ListBox-Steuerelement.
Der Code zum Implementieren dieses Layouts ist recht simpel. Das Stammelement ist ein DockPanel und verfügt über zwei untergeordnete Elemente. Das erste ist ein Border-Element, welches das ListBox-Steuerelement hostet. Es belegt einen 200x200 großen quadratischen Bereich in der oberen rechten Ecke der Seite. Das zweite ist ein StackPanel-Element, das einen Satz WPF-Steuerelemente enthält, die Informationen anzeigen und Interoperations-Eigenschaften verfügbar machen, mit deren Hilfe Sie das ListBox-Steuerelement anpassen können. Details über die Natur und Funktion der einzelnen untergeordneten Elemente des StackPanel können Sie dem jeweiligen Referenzmaterial entnehmen. Sie werden im nachfolgenden Beispielcode aufgeführt, aber an dieser Stelle nicht weiter erläutert (das grundlegende Interoperations-Modell benötigt keins dieser Elemente, sie sollen dem Beispiel lediglich mehr Interaktivität verleihen).
<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 ControlHost.cs, die das Steuerelement tatsächlich hostet. Er erbt von HwndHost. Der Konstruktor akzeptiert zwei Parameter, Höhe und Breite, die der Höhe und Breite des Border-Elements, welches das ListBox-Steuerelement hostet, entsprechen. Diese Werte werden später verwendet, um sicherzustellen, dass die Größe des Steuerelements der des Border-Elements 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
Des Weiteren gibt es auch einen Satz von Konstanten. Diese Konstanten sind zum größten Teil aus Winuser.h entlehnt, und ermöglichen Ihnen die Verwendung konventioneller Namen beim Aufrufen von Win32-Funktionen.
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 von BuildWindowCore zum Erstellen des Microsoft Win32-Fensters
Überschreiben Sie diese Methode, um das von der Seite zu hostende Win32-Fenster zu erstellen und die Verbindung zwischen Fenster und Seite herzustellen. Da dieses Beispiel das Hosten eines ListBox-Steuerelements umfasst, werden zwei Fenster erstellt. Die erste ist das Fenster, das tatsächlich von der WPF-Seite gehostet wird. Das ListBox-Steuerelement wird als untergeordnetes Element dieses Fensters erstellt.
Dieser Ansatz wurde bewusst gewählt, um den Empfang von Benachrichtigungen aus dem Steuerelement zu vereinfachen. Die HwndHost-Klasse ermöglicht Ihnen die Verarbeitung von Nachrichten, die zum hostenden Fenster gesendet wurden. Wenn Sie ein Win32-Steuerelement direkt hosten, erhalten Sie die zur internen Nachrichtenschleife des Steuerelements gesendeten Nachrichten. Sie können das Steuerelement anzeigen und ihm Nachrichten senden, aber Sie erhalten keine der 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 machen Sie das Steuerelement zu einem untergeordneten Element dieses Fensters. Dadurch können Sie sowohl Nachrichten für das Hostfenster als auch vom Steuerelement gesendete Benachrichtigungen verarbeiten. Da das Hostfenster kaum mehr als ein einfacher Wrapper für das Steuerelement ist, bezeichnen wir das Gesamtpaket der Einfachheit halber künftig als ListBox-Steuerelement.
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, und so weiter. Ein wesentlicher einfacherer Ansatz ist jedoch, ein Fenster mithilfe der vordefinierten „statischen“ Fensterklasse zu definieren. Dadurch erhalten Sie mit minimalem Codieraufwand die Fensterprozedur, die Sie benötigen, um Benachrichtigungen vom Steuerelement zu empfangen.
Das HWND des Steuerelements wird über eine schreibgeschützte Eigenschaft verfügbar gemacht, sodass es von der Hostseite zum Senden von Nachrichten an das Steuerelement verwendet werden 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 wie oben erläutert auf die an den Konstruktor übergebenen Werte festgelegt. Dadurch wird sichergestellt, dass die Größe von Hostfenster und Steuerelement exakt dem auf der Seite reservierten Bereich entspricht. Nachdem die Fenster erstellt wurden, gibt das Beispiel ein HandleRef-Objekt zurück, welches das 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 des HwndHost überschreiben. In diesem Beispiel werden die Nachrichten für das Steuerelement durch die MessageHook-Handler behandelt, wodurch sich die Implementierung von WndProc und DestroyWindowCore auf ein Minimum beschränkt. Im Fall von WndProc legen Sie handled
auf false
fest, um anzugeben, dass die Meldung nicht verarbeitet wurde und 0 zurückgegeben wird. Bei 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
Hosten 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 Border-Elements, welches das Steuerelement enthält ( ControlHostElement
), an den ControlHost
-Konstruktor. Dadurch wird sichergestellt, dass das Listenfeld die richtige Größe hat. Sie können das Steuerelement auf der Seite zu hosten, indem Sie das ControlHost
-Objekt der Child-Eigenschaft des Host-Border.
Das Beispiel fügt einen Ereignishandler für das MessageHook-Ereignis der ControlHost
an, 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 sind dies die Nachrichten, die an das das eigentliche ListBox-Steuerelement umschließende Fenster gesendet werden, einschließlich der Benachrichtigungen aus dem Steuerelement. Das Beispiel ruft die SendMessage-Methode auf, um Informationen aus dem Steuerelement abzurufen und dessen Inhalt zu ändern. Wie die Seite im Detail mit dem Steuerelement kommuniziert, wird im nächsten Abschnitt erläutert.
Hinweis
Beachten Sie, dass es zwei PInvoke-Deklarationen für SendMessage gibt. Dies ist erforderlich, da eine den wParam
-Parameter verwendet, um eine Zeichenfolge zu übergeben und die andere, um einen Integer zu übergeben. Um sicherzustellen, dass die Daten ordnungsgemäß gemarshallt werden, wird daher eine separate Deklaration für jede Signatur benötigt.
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
Implementieren der Kommunikation zwischen dem Steuerelement und der Seite
Sie bearbeiten das Steuerelement, indem Sie ihm Windows-Nachrichten senden. Das Steuerelement benachrichtigt Sie, wenn der Benutzer durch Senden von Benachrichtigungen an das Hostfenster mit ihm interagiert. Das Beispiel Hosten eines Win32-ListBox-Steuerelements in WPF umfasst eine Benutzeroberfläche, die diese Funktionsweise in mehreren Beispielen veranschaulicht:
Der Liste ein Element hinzufügen.
Das markierte Element aus der Liste löschen
Den Text des aktuell markierten Elements anzeigen.
Die Anzahl der Elemente in der Liste anzeigen.
Der Benutzer kann im Listenfeld auch ein Element auswählen, indem er darauf klickt, genau wie bei einer herkömmlichen Win32-Anwendung. Die angezeigten Daten werden bei jeder Zustandsänderung des Listenfelds durch den Benutzer aktualisiert, egal ob ein Element ausgewählt, hinzugefügt oder angefügt wurde.
Um Elemente anzufügen, senden Sie dem Listenfeld eine LB_ADDSTRING
-Nachricht. Um Elemente zu löschen, senden Sie zunächst LB_GETCURSEL
, um den Index der aktuellen Auswahl abzurufen, und dann LB_DELETESTRING
, um das Element zu löschen. Das Beispiel sendet auch LB_GETCOUNT
, und nutzt den zurückgegebenen Wert zum Aktualisieren der Anzeige, die die Anzahl der Elemente angibt. Beide dieser Instanzen von SendMessage
verwenden eine der PInvoke-Deklarationen, die im vorherigen Abschnitt erläutert wurden.
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
Sobald der Benutzer ein Element auswählt oder die bestehende 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. Er gibt zudem einen Verweis auf einen booleschen Wert, handled
, weiter. Sie setzen den Wert für handled
auf true
, um anzugeben, dass die Meldung verarbeitet wurde und keine weitere Verarbeitung erforderlich ist.
WM_COMMAND
wird aus verschiedensten Gründen gesendet, weshalb 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 oberen Wort des wParam
-Parameters enthalten. Im Beispiel werden bitweise Operatoren, um die ID zu extrahieren. Wenn der Benutzer eine Auswahl vorgenommen oder geändert hat, wird die LBN_SELCHANGE
sein.
Wenn LBN_SELCHANGE
empfangen wird, ruft das Beispiel den Index des ausgewählten Elements ab, indem es eine LB_GETCURSEL
-Nachricht an das Steuerelements sendet. Zum Abrufen des Texts erstellen Sie zuerst einen StringBuilder. Anschließend senden Sie eine LB_GETTEXT
-Nachricht an das Steuerelement. Übergeben Sie das leere StringBuilder-Objekt als wParam
-Parameter. Wenn SendMessage
zurückgegeben wird, enthält der StringBuilder den Text des ausgewählten Elements. Diese Verwendung von SendMessage
erfordert eine weitere PInvoke-Deklaration.
Setzen Sie abschließend den Wert von handled
auf true
, um anzugeben, dass die Meldung verarbeitet wurde.
Weitere Informationen
.NET Desktop feedback