Esercitazione: hosting di oggetti visivi in un'applicazione Win32

Windows Presentation Foundation (WPF) offre un ambiente avanzato per la creazione di applicazioni. Tuttavia, quando si ha un notevole investimento nel codice Win32, potrebbe essere più efficace aggiungere funzionalità WPF all'applicazione invece di riscrivere il codice. Per fornire supporto per i sottosistemi grafici Win32 e WPF usati simultaneamente in un'applicazione, WPF fornisce un meccanismo per l'hosting di oggetti in una finestra Win32.

Questa esercitazione descrive come scrivere un'applicazione di esempio, Hit Test con l'esempio di interoperabilità Win32, che ospita oggetti visivi WPF in una finestra Win32.

Requisiti

Questa esercitazione presuppone una familiarità di base con la programmazione WPF e Win32. Per un'introduzione di base alla programmazione WPF, vedere Procedura dettagliata: Prima applicazione desktop WPF. Per un'introduzione alla programmazione Win32, vedi uno dei numerosi libri sull'argomento, in particolare Programmazione di Windows di Charles Petzold.

Nota

Questa esercitazione include numerosi esempi di codice relativi all'esempio associato. Tuttavia, per una questione di leggibilità, il codice di esempio completo non è compreso. Per il codice di esempio completo, vedere Hit Test with Win32 Interoperation Sample (Esempio di hit test con interoperatività Win32).

Creazione di una finestra Win32 host

La chiave per ospitare oggetti WPF in una finestra Win32 è la HwndSource classe . Questa classe esegue il wrapping degli oggetti WPF in una finestra Win32, consentendo di incorporarli nell'interfaccia utente come finestra figlio.

L'esempio seguente illustra il codice per la creazione dell'oggetto HwndSource come finestra del contenitore Win32 per gli oggetti visivi. Per impostare lo stile della finestra, la posizione e altri parametri per la finestra Win32, usare l'oggetto HwndSourceParameters .

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

Nota

Il valore della ExtendedWindowStyle proprietà non può essere impostato su WS_EX_TRANSPARENT. Ciò significa che la finestra Win32 host non può essere trasparente. Per questo motivo, il colore di sfondo della finestra Win32 host viene impostato sullo stesso colore di sfondo della finestra padre.

Aggiunta di oggetti visivi alla finestra Win32 host

Dopo aver creato una finestra contenitore Win32 host per gli oggetti visivi, è possibile aggiungervi oggetti visivi. È necessario assicurarsi che qualsiasi trasformazione degli oggetti visivi, ad esempio le animazioni, non si estenda oltre i limiti del rettangolo di delimitazione della finestra Win32 host.

Nell'esempio seguente viene illustrato il codice per la creazione dell'oggetto e l'aggiunta HwndSource di oggetti visivi.

Nota

La RootVisual proprietà dell'oggetto HwndSource viene impostata sul primo oggetto visivo aggiunto alla finestra Win32 host. L'oggetto visivo radice definisce il nodo di primo livello della struttura ad albero di oggetti visivi. Tutti gli oggetti visivi successivi aggiunti alla finestra Host Win32 vengono aggiunti come oggetti figlio.

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

Implementazione del filtro messaggi Win32

La finestra Host Win32 per gli oggetti visivi richiede una routine di filtro messaggi finestra per gestire i messaggi inviati alla finestra dalla coda dell'applicazione. La procedura della finestra riceve messaggi dal sistema Win32. Può trattarsi di messaggi di input o di messaggi di gestione della finestra. È possibile facoltativamente gestire un messaggio nella routine della finestra oppure passarlo al sistema per l'elaborazione predefinita.

L'oggetto HwndSource definito come padre per gli oggetti visivi deve fare riferimento alla routine di filtro messaggi della finestra specificata. Quando si crea l'oggetto HwndSource , impostare la HwndSourceHook proprietà per fare riferimento alla routine della finestra.

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

L'esempio seguente illustra il codice per la gestione dei messaggi di rilascio dei pulsanti sinistro e destro del mouse. Il valore della coordinata della posizione di clic del mouse è contenuto nel valore del lParam parametro .

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

Elaborazione dei messaggi Win32

Il codice nell'esempio seguente mostra come viene eseguito un hit test sulla gerarchia degli oggetti visivi contenuti nella finestra Win32 dell'host. È possibile identificare se un punto si trova all'interno della geometria di un oggetto visivo usando il HitTest metodo per specificare l'oggetto visivo radice e il valore della coordinata su cui eseguire il hit test. In questo caso, l'oggetto visivo radice è il valore della RootVisual proprietà dell'oggetto HwndSource .

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

Per altre informazioni sull'hit testing su oggetti visivi, vedere Hit testing a livello visivo.

Vedi anche