Tutorial: Hosten von visuellen Objekten in einer Win32-Anwendung

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 neu zu schreiben. Zur Unterstützung von Win32- und WPF-Grafiksubsystemen, die gleichzeitig in einer Anwendung verwendet werden, bietet WPF einen Mechanismus zum Hosten von Objekten in einem Win32-Fenster.

In diesem Tutorial wird beschrieben, wie Sie eine Beispielanwendung, Beispiel für Treffertests mit Win32-Interoperabilität, schreiben können, die visuelle WPF-Objekte in einem Win32-Fenster hostet.

Anforderungen

In diesem Tutorial wird vorausgesetzt, dass Sie mit den Grundlagen der WPF-und Win32-Programmierung vertraut sind. Eine grundlegende Einführung in die WPF-Programmierung finden Sie in Exemplarische Vorgehensweise: Meine erste WPF-Desktopanwendung. Eine Einführung in die Win32-Programmierung finden Sie in zahlreichen Büchern zu diesem Thema, insbesondere in Programming Windows von Charles Petzold.

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 Beispielcode finden Sie in Beispiel für Treffertests mit Win32-Interoperabilität.

Erstellen des Host-Win32-Fensters

Entscheidend für das Hosten von WPF-Objekten in einem Win32-Fenster ist die HwndSource-Klasse. Diese Klasse umschließt die WPF-Objekte in einem Win32-Fenster, sodass sie als untergeordnetes Fenster in Ihre Benutzeroberfläche eingebunden werden können.

Das folgende Beispiel zeigt den Code zum Erstellen des HwndSource-Objekts als Win32-Containerfenster für visuelle Objekte. Zum Festlegen von Fensterstils, Position und anderer Parameter für das Win32-Fenster verwenden Sie das HwndSourceParameters-Objekt.

// Constant values from the "winuser.h" header file.
internal const int WS_CHILD = 0x40000000,
                   WS_VISIBLE = 0x10000000;

internal static void CreateHostHwnd(IntPtr parentHwnd)
{
    // Set up the parameters for the host hwnd.
    HwndSourceParameters parameters = new HwndSourceParameters("Visual Hit Test", _width, _height);
    parameters.WindowStyle = WS_VISIBLE | WS_CHILD;
    parameters.SetPosition(0, 24);
    parameters.ParentWindow = parentHwnd;
    parameters.HwndSourceHook = new HwndSourceHook(ApplicationMessageFilter);

    // Create the host hwnd for the visuals.
    myHwndSource = new HwndSource(parameters);

    // Set the hwnd background color to the form's background color.
    myHwndSource.CompositionTarget.BackgroundColor = System.Windows.Media.Brushes.OldLace.Color;
}
' Constant values from the "winuser.h" header file.
Friend Const WS_CHILD As Integer = &H40000000, WS_VISIBLE As Integer = &H10000000

Friend Shared Sub CreateHostHwnd(ByVal parentHwnd As IntPtr)
    ' Set up the parameters for the host hwnd.
    Dim parameters As New HwndSourceParameters("Visual Hit Test", _width, _height)
    parameters.WindowStyle = WS_VISIBLE Or WS_CHILD
    parameters.SetPosition(0, 24)
    parameters.ParentWindow = parentHwnd
    parameters.HwndSourceHook = New HwndSourceHook(AddressOf ApplicationMessageFilter)

    ' Create the host hwnd for the visuals.
    myHwndSource = New HwndSource(parameters)

    ' Set the hwnd background color to the form's background color.
    myHwndSource.CompositionTarget.BackgroundColor = System.Windows.Media.Brushes.OldLace.Color
End Sub

Hinweis

Der Wert der ExtendedWindowStyle-Eigenschaft kann nicht auf WS_EX_TRANSPARENT festgelegt werden. Dies bedeutet, dass das Host-Win32-Fenster nicht transparent sein kann. Aus diesem Grund wird die Hintergrundfarbe des Host-Win32-Fensters auf die Hintergrundfarbe seines übergeordneten Fensters festgelegt.

Hinzufügen von visuellen Objekten zum Host-Win32-Fenster

Nach Erstellen eines Host-Win32-Containerfensters für die visuellen Objekte können Sie diesem visuelle Objekte hinzufügen. Sie sollten sicherstellen, dass alle Transformationen der visuellen Objekte, z. B. Animationen, sich nicht über die Grenzen des das Host-Win32-Fenster umschließenden Rechtecks erstrecken.

Das folgende Beispiel zeigt den Code zum Erstellen des HwndSource-Objekts und Hinzufügen von visuellen Objekten.

Hinweis

Die RootVisual-Eigenschaft des HwndSource-Objekts wird auf das erste visuelle Objekt festgelegt, das dem Host-Win32-Fenster hinzugefügt wurde. Das visuelle Stammobjekt definiert den obersten Knoten der Struktur des visuellen Objekts. Alle weiteren visuellen Objekte, die dem Host-Win32-Fenster hinzugefügt werden, werden als untergeordnete Objekte hinzugefügt.

public static void CreateShape(IntPtr parentHwnd)
{
    // Create an instance of the shape.
    MyShape myShape = new MyShape();

    // Determine whether the host container window has been created.
    if (myHwndSource == null)
    {
        // Create the host container window for the visual objects.
        CreateHostHwnd(parentHwnd);

        // Associate the shape with the host container window.
        myHwndSource.RootVisual = myShape;
    }
    else
    {
        // Assign the shape as a child of the root visual.
        ((ContainerVisual)myHwndSource.RootVisual).Children.Add(myShape);
    }
}
Public Shared Sub CreateShape(ByVal parentHwnd As IntPtr)
    ' Create an instance of the shape.
    Dim myShape As New MyShape()

    ' Determine whether the host container window has been created.
    If myHwndSource Is Nothing Then
        ' Create the host container window for the visual objects.
        CreateHostHwnd(parentHwnd)

        ' Associate the shape with the host container window.
        myHwndSource.RootVisual = myShape
    Else
        ' Assign the shape as a child of the root visual.
        CType(myHwndSource.RootVisual, ContainerVisual).Children.Add(myShape)
    End If
End Sub

Implementieren des Win32-Nachrichtenfilters

Das Host-Win32-Fenster für visuelle Objekte erfordert eine Filterprozedur für Fenstermeldungen, um Meldungen zu verarbeiten, die aus der Anwendungswarteschlange an das Fenster gesendet werden. Die Fensterprozedur empfängt Meldungen vom Win32-System. Dabei kann es sich um Eingabe- oder Fensterverwaltungsmeldungen handeln. Sie können wahlweise eine Meldung in der Fensterprozedur verarbeiten oder die Meldung zwecks Standardverarbeitung an das System übergeben.

Das HwndSource-Objekt, das Sie als übergeordnetes Element für die visuellen Objekte definiert haben, muss auf die Fenstermeldungsfilterprozedur verweisen, die Sie angeben. Wenn Sie das HwndSource-Objekt erstellen, legen Sie die HwndSourceHook-Eigenschaft fest, um auf die Fensterprozedur zu verweisen.

parameters.HwndSourceHook = new HwndSourceHook(ApplicationMessageFilter);
parameters.HwndSourceHook = New HwndSourceHook(AddressOf ApplicationMessageFilter)

Das folgende Beispiel zeigt den Code für die Verarbeitung der Meldungen der linken und rechten Maustaste. Der Koordinatenwert der Mausposition befindet sich im Wert des lParam-Parameters.

// Constant values from the "winuser.h" header file.
internal const int WM_LBUTTONUP = 0x0202,
                   WM_RBUTTONUP = 0x0205;

internal static IntPtr ApplicationMessageFilter(
    IntPtr hwnd, int message, IntPtr wParam, IntPtr lParam, ref bool handled)
{
    // Handle messages passed to the visual.
    switch (message)
    {
        // Handle the left and right mouse button up messages.
        case WM_LBUTTONUP:
        case WM_RBUTTONUP:
            System.Windows.Point pt = new System.Windows.Point();
            pt.X = (uint)lParam & (uint)0x0000ffff;  // LOWORD = x
            pt.Y = (uint)lParam >> 16;               // HIWORD = y
            MyShape.OnHitTest(pt, message);
            break;
    }

    return IntPtr.Zero;
}
' Constant values from the "winuser.h" header file.
Friend Const WM_LBUTTONUP As Integer = &H202, WM_RBUTTONUP As Integer = &H205

Friend Shared Function ApplicationMessageFilter(ByVal hwnd As IntPtr, ByVal message As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr, ByRef handled As Boolean) As IntPtr
    ' Handle messages passed to the visual.
    Select Case message
        ' Handle the left and right mouse button up messages.
        Case WM_LBUTTONUP, WM_RBUTTONUP
            Dim pt As New System.Windows.Point()
            pt.X = CUInt(lParam) And CUInt(&HFFFF) ' LOWORD = x
            pt.Y = CUInt(lParam) >> 16 ' HIWORD = y
            MyShape.OnHitTest(pt, message)
    End Select

    Return IntPtr.Zero
End Function

Verarbeiten der Win32-Meldungen

Der Code im folgenden Beispiel zeigt, wie ein Treffertest für die Hierarchie der visuellen Objekte durchgeführt wird, die im Host-Win32-Fenster enthalten sind. Sie können erkennen, ob sich ein Punkt innerhalb der Geometrie eines Objekts befindet, indem Sie mit der HitTest-Methode das visuelle Stammobjekt und den Koordinatenwert für den Treffertest angeben. In diesem Fall ist das visuelle Stammobjekt der Wert der RootVisual-Eigenschaft des HwndSource-Objekts.

// Constant values from the "winuser.h" header file.
public const int WM_LBUTTONUP = 0x0202,
                 WM_RBUTTONUP = 0x0205;

// Respond to WM_LBUTTONUP or WM_RBUTTONUP messages by determining which visual object was clicked.
public static void OnHitTest(System.Windows.Point pt, int msg)
{
    // Clear the contents of the list used for hit test results.
    hitResultsList.Clear();

    // Determine whether to change the color of the circle or to delete the shape.
    if (msg == WM_LBUTTONUP)
    {
        MyWindow.changeColor = true;
    }
    if (msg == WM_RBUTTONUP)
    {
        MyWindow.changeColor = false;
    }

    // Set up a callback to receive the hit test results enumeration.
    VisualTreeHelper.HitTest(MyWindow.myHwndSource.RootVisual,
                             null,
                             new HitTestResultCallback(CircleHitTestResult),
                             new PointHitTestParameters(pt));

    // Perform actions on the hit test results list.
    if (hitResultsList.Count > 0)
    {
        ProcessHitTestResultsList();
    }
}
' Constant values from the "winuser.h" header file.
Public Const WM_LBUTTONUP As Integer = &H0202, WM_RBUTTONUP As Integer = &H0205

' Respond to WM_LBUTTONUP or WM_RBUTTONUP messages by determining which visual object was clicked.
Public Shared Sub OnHitTest(ByVal pt As System.Windows.Point, ByVal msg As Integer)
    ' Clear the contents of the list used for hit test results.
    hitResultsList.Clear()

    ' Determine whether to change the color of the circle or to delete the shape.
    If msg = WM_LBUTTONUP Then
        MyWindow.changeColor = True
    End If
    If msg = WM_RBUTTONUP Then
        MyWindow.changeColor = False
    End If

    ' Set up a callback to receive the hit test results enumeration.
    VisualTreeHelper.HitTest(MyWindow.myHwndSource.RootVisual, Nothing, New HitTestResultCallback(AddressOf CircleHitTestResult), New PointHitTestParameters(pt))

    ' Perform actions on the hit test results list.
    If hitResultsList.Count > 0 Then
        ProcessHitTestResultsList()
    End If
End Sub

Weitere Informationen über Treffertests für visuelle Objekte finden Sie unter Treffertests in der visuellen Ebene.

Weitere Informationen