자습서: Win32 애플리케이션에서 시각적 개체 호스팅

WPF(Windows Presentation Foundation)는 애플리케이션을 만들기 위한 풍부한 환경을 제공합니다. 그러나 Win32 코드에 상당한 투자를 한 경우 코드를 다시 작성하는 대신 애플리케이션에 WPF 기능을 추가하는 것이 더 효과적일 수 있습니다. 애플리케이션에서 동시에 사용되는 Win32 및 WPF 그래픽 하위 시스템에 대한 지원을 제공하기 위해 WPF는 Win32 창에서 개체를 호스팅하는 메커니즘을 제공합니다.

이 자습서에서는 WPF 시각적 개체를 Win32 창에 호스트하는 Win32 상호 운용성을 사용한 적중 테스트 샘플 애플리케이션을 작성하는 방법을 설명합니다.

요구 사항

이 자습서에서는 WPF 및 Win32 프로그래밍에 대한 기본 지식이 있다고 가정합니다. WPF 프로그래밍에 대한 기본적인 소개 내용을 보려면 연습: 내 첫 WPF 데스크톱 애플리케이션을 참조하세요. Win32 프로그래밍 소개를 보려면 해당 주제와 관련된 많은 서적, 특히 Charles Petzold가 저술한 Programming Windows를 참조하세요.

참고

이 자습서에는 관련 샘플의 많은 코드 예제가 포함되어 있습니다. 그러나 가독성을 위해 전체 샘플 코드를 포함하지는 않습니다. 전체 샘플 코드를 보려면 Win32 상호 운용성을 사용하는 적중 테스트 샘플을 참조하세요.

호스트 Win32 창 만들기

Win32 창에서 WPF 개체를 호스팅하는 키는 HwndSource 클래스입니다. 이 클래스는 Win32 창에서 WPF 개체를 래핑하여 사용자 인터페이스(UI)에 자식 창으로 통합될 수 있도록 합니다.

다음 예제에서는 HwndSource 개체를 시각적 개체에 대한 Win32 컨테이너 창으로 만들기 위한 코드를 보여줍니다. Win32 창에 대한 창 스타일, 위치 및 그 외 매개 변수를 설정하려면 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

참고

ExtendedWindowStyle 속성 값을 WS_EX_TRANSPARENT로 설정할 수 없습니다. 즉, 호스트 Win32 창은 투명할 수 없습니다. 이러한 이유로 호스트 Win32 창의 배경색은 부모 창과 같은 배경색으로 설정됩니다.

호스트 Win32 창에 시각적 개체 추가

시각적 개체에 대한 호스트 Win32 컨테이너 창을 만든 후에는 이 창에 시각적 개체를 추가할 수 있습니다. 필요하다면 애니메이션과 같은 시각적 개체의 변환이 호스트 Win32 창의 경계 사각형 범위를 벗어나 확장되지 않도록 해야 합니다.

다음 예제에서는 HwndSource 개체를 만들고 여기에 시각적 개체를 추가하는 코드를 보여줍니다.

참고

HwndSource 개체의 RootVisual 속성은 호스트 Win32 창에 추가된 첫 번째 시각적 개체로 설정됩니다. 루트 시각적 개체는 시각적 개체 트리의 최상위 노드를 정의합니다. 이후에 호스트 Win32 창에 추가되는 시각적 개체는 자식 개체로 추가됩니다.

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

Win32 메시지 필터 구현

시각적 개체에 대한 호스트 Win32 창에는 애플리케이션 큐에서 창으로 전송된 메시지를 처리하기 위한 창 메시지 필터 절차가 필요합니다. 이러한 창 절차는 Win32 시스템에서 메시지를 수신합니다. 이러한 메시지를 입력 메시지이거나 창 관리 메시지일 수 있습니다. 필요에 따라 창 절차에서 메시지를 처리하거나 기본 처리를 위해 메시지를 시스템으로 전달할 수 있습니다.

시각적 개체의 부모로 정의한 HwndSource 개체는 제공된 창 메시지 필터 절차를 참조해야 합니다. HwndSource 개체를 생성할 때 창 프로시저를 참조하도록 HwndSourceHook 속성을 설정합니다.

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

다음 예제에서는 왼쪽 및 오른쪽 마우스 단추 잠금 메시지를 처리하기 위한 코드를 보여 줍니다. 마우스 적중 위치의 좌표 값은 lParam 매개 변수 값에 포함됩니다.

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

Win32 메시지 처리

다음 예제의 코드는 호스트 Win32 창에 포함된 시각적 개체의 계층 구조에 대해 적중 테스트를 수행하는 방법을 보여줍니다. HitTest 메서드를 사용하여 적중 테스트를 수행할 루트 시각적 개체 및 좌표 값을 지정하면 한 지점이 시각적 개체의 기하 도형 내에 있는지 여부를 확인할 수 있습니다. 이 경우 루트 시각적 개체는 HwndSource 개체의 RootVisual 속성 값입니다.

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

시각적 개체에 대한 적중 테스트 관련 내용은 시각적 계층에서 적중 테스트를 참조하세요.

참고 항목