카테고리 없음

VS2022 MFC U/I - WPF U/I 붙이기 - 3

folcjin 2025. 5. 17. 11:40
728x90
반응형

MFC 애플리케이션은 기존처럼 CView, CDialog 기반으로 유지하면서, 그 내부에 WPF 컨트롤을 child 형태로 삽입(hosting) 하는 방식입니다. 이를 좀 더 구조적으로 설명드리면 다음과 같습니다:

 

기본 구조

[MFC 메인 윈도우 또는 다이얼로그]
    └─ [WPF 컨트롤을 호스팅하는 C++/CLI HwndHost 클래스]
         └─ [WPF UserControl (XAML UI)]

 

  • MFC에서 HWND를 통해 자식 윈도우처럼 공간을 만들고
  • C++/CLI의 HwndHost를 사용해 WPF 컨트롤을 MFC 영역 안에 마운트
  • 즉, WPF는 child dialog 또는 embedded panel처럼 작동합니다.

 

왜 이렇게 구성하는가?

이유설명
기존 MFC 구조 유지 전체 재작성 없이도 WPF 기능 확장 가능
강력한 UI 표현력 확보 그래픽, 애니메이션, 바인딩 등 WPF 장점 활용
Interop 구조 단순화 HWND 기반으로 명확한 창 구조 가능
확장성 기존 기능 위에만 선택적 WPF UI 추가 가능 (설정창, 뷰어 등)
 

 

UI 구조 예시

만약 MFC 메인 창이 CDialogEx라면:

BOOL CMainDlg::OnInitDialog()
{
    CDialogEx::OnInitDialog();

    // WPF UserControl 호스팅
    m_wpfControl = gcnew WpfHostBridge::WpfHostControl();
    m_wpfControl->OnButtonClicked += gcnew WpfHostBridge::ButtonClickedHandler(this, &CMainDlg::OnWpfButtonClicked);

    m_wpfControl->CreateHandle();
    HWND hWpf = (HWND)m_wpfControl->Handle.ToPointer();

    ::SetParent(hWpf, this->m_hWnd);
    ::MoveWindow(hWpf, 20, 20, 300, 200, TRUE);

    return TRUE;
}

이때 hWpf는 MFC 다이얼로그의 자식 윈도우(WS_CHILD)처럼 동작하며, 마치 기존 CStatic, CEdit 등을 붙이듯 배치 가능합니다.

어떤 상황에 유리한가?

  • 기존 MFC 프로젝트에 부분적으로만 WPF 도입하고 싶은 경우
  • 신규 기능(그래프, 애니메이션 UI, 대화형 설정 등)을 신속히 구현하고자 할 때
  • 단계적 현대화 전략을 취할 때 (기존 시스템 유지하면서 UI를 개선)

 

결론

"기존 MFC 애플리케이션 내부에, WPF를 child dialog 형태로 임베딩한다."

 

 

 

< 샘플 코드 >

 

/*

  • Sample Visual Studio Solution: MfcWpfDialogSample
  • Projects:
    1. WpfControlLibrary (.NET Framework 4.8 Class Library)
    1. WpfHostBridge (C++/CLI Class Library)
    1. MfcHostApp (MFC Dialog-based Application)
      */

// ----------------------------------------------------
// Project: WpfControlLibrary (Class Library)
// File: MyWpfControl.xaml
// ----------------------------------------------------





// ----------------------------------------------------
// File: MyWpfControl.xaml.cs
// ----------------------------------------------------
using System;
using System.Windows;
using System.Windows.Controls;

namespace WpfControlLibrary
{
public partial class MyWpfControl : UserControl
{
// Event exposed to host
public event EventHandler Clicked;

    public MyWpfControl()
    {
        InitializeComponent();
    }

    private void btnClick_Click(object sender, RoutedEventArgs e)
    {
        Clicked?.Invoke(this, EventArgs.Empty);
    }
}

}

// ----------------------------------------------------
// Project: WpfHostBridge (C++/CLI Class Library)
// File: WpfHostControl.h
// ----------------------------------------------------
#pragma once

using namespace System;
using namespace System::Windows;
using namespace System::Windows::Interop;

namespace WpfHostBridge {

// Delegate to notify host
public delegate void ButtonClickedHandler();

// HwndHost-derived control for embedding WPF
public ref class WpfHostControl : HwndHost
{
private:
    HwndSource^ _source;
    WpfControlLibrary::MyWpfControl^ _wpf;

public:
    // Exposed event
    event ButtonClickedHandler^ OnButtonClicked;

    WpfHostControl() {}

protected:
    // Build the WPF child
    virtual IntPtr BuildWindowCore(HandleRef hwndParent) override
    {
        _wpf = gcnew WpfControlLibrary::MyWpfControl();
        _wpf->Clicked += gcnew EventHandler(this, &WpfHostControl::WpfClicked);

        HwndSourceParameters^ params = gcnew HwndSourceParameters("WPFHost");
        params->PositionX = 0;
        params->PositionY = 0;
        params->Height = 200;
        params->Width = 300;
        params->ParentWindow = hwndParent.Handle;
        params->WindowStyle = static_cast<int>(WindowStyles::Child) | static_cast<int>(WindowStyles::Visible);

        _source = gcnew HwndSource(*params);
        _source->RootVisual = _wpf;

        return IntPtr(_source->Handle);
    }

    virtual void DestroyWindowCore(HandleRef hwnd) override
    {
        delete _source;
    }

private:
    void WpfClicked(Object^ s, EventArgs^ e)
    {
        // Forward to host
        OnButtonClicked();
    }
};

}

// ----------------------------------------------------
// Project: MfcHostApp (MFC Dialog-based Application)
// File: MainDlg.h
// ----------------------------------------------------
#pragma once
#include <afxext.h>
#include <vcclr.h>

// Import the C++/CLI assembly reference
#using <WpfHostBridge.dll>

// Message for WPF event (optional)
#define WM_WPF_BUTTON_CLICKED (WM_USER + 101)

class CMainDlg : public CDialogEx
{
public:
enum { IDD = IDD_MAINDLG };

CMainDlg(CWnd* pParent = nullptr);

protected:
virtual BOOL OnInitDialog();
afx_msg void OnDestroy();

// Handler for WPF button click
void OnWpfButtonClicked();

DECLARE_MESSAGE_MAP()

private:
// Managed handle to the WPF host control
gcrootWpfHostBridge::WpfHostControl^ m_wpfControl;
HWND m_hWpf;
};

// ----------------------------------------------------
// File: MainDlg.cpp
// ----------------------------------------------------
#include "pch.h"
#include "MainDlg.h"
#include "afxdialogex.h"

BEGIN_MESSAGE_MAP(CMainDlg, CDialogEx)
ON_WM_DESTROY()
END_MESSAGE_MAP()

CMainDlg::CMainDlg(CWnd* pParent)
: CDialogEx(IDD_MAINDLG, pParent)
{
}

BOOL CMainDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();

// Enable Visual Styles for CLR
System::Windows::Forms::Application::EnableVisualStyles();

// Create WPF host via C++/CLI
m_wpfControl = gcnew WpfHostBridge::WpfHostControl();
m_wpfControl->OnButtonClicked += gcnew WpfHostBridge::ButtonClickedHandler(this, &CMainDlg::OnWpfButtonClicked);

// Build the HWND
IntPtr handle = m_wpfControl->Handle;
m_hWpf = static_cast<HWND>(handle.ToPointer());

// Parent to MFC dialog and position
::SetParent(m_hWpf, this->GetSafeHwnd());
::MoveWindow(m_hWpf, 20, 20, 300, 200, TRUE);

return TRUE;

}

void CMainDlg::OnWpfButtonClicked()
{
// Called when WPF button is clicked
AfxMessageBox(_T("WPF 버튼이 클릭되었습니다!"));
}

void CMainDlg::OnDestroy()
{
CDialogEx::OnDestroy();

// Clean up
m_wpfControl = nullptr;

}

 

728x90
반응형
사업자 정보 표시펼치기/접기
사업자 등록번호 : -- | TEL : --