亀岡的プログラマ日記

京都のベッドタウン、亀岡よりだらだらとお送りいたします。

MFCにWindowsFormコントロールを埋め込む

いやはや、いまさらMFCですよ、おっかさん。

…なんて言ったら怒られますよね。実際まぁ、個々のクラスに問題あるとはいえ、個人的にはせめてQtでも使いたいとはいえ、まぁその、Windowsアプリを作る上での有力な選択肢の一つ、ですよね。

まぁぶっちゃけて言うと、レガシーコード*1MFCであることが多いんですけどね。

んでまぁ、やっぱGUIは.NETで書いたほうがずいぶんと楽なわけで、そこをパスする方法が必要ですよね。

非常に真面目にやるなら、COM連携機能を活用してActiveXコントロールを作ってしまえばよろしい。CodeProjectには結構サンプルがあります。

ですが、めんどくさいです。COMとかもうちょっとどうにかしていていただきたいです。その時に使えるのが、以下の記事のお話。

これは何かというと、

  1. afxwinforms.hを参照する(もちろん/clrコンパイラオプションが必要)
  2. CWinFormsControl<>クラスで.NETのコントロールをホストする
  3. DDX_ManagedControlを用いて、任意のMFCコントロールをDDX使って.NETコントロールにすげ替える。

という手順でもって、.NETのクラスをMFCのコントロールの中に埋め込むことができる、というものです。まぁ、C++/CLIですな。

ちょっとコードと一緒に見てみる

afxwinforms.hを参照する

afxcmn.hの下にしれっと加えておきます。普通に作っていればstdafx.hに作りますね。

#ifndef _AFX_NO_AFXCMN_SUPPORT
#include <afxcmn.h>             // MFC の Windows コモン コントロール サポート
#include <afxwinforms.h>   // MFC WindowsFormのサポート
#endif // _AFX_NO_AFXCMN_SUPPORT

これをインクルードした瞬間に、/clrコンパイラオプションが必須になるので、お気をつけて。

CWinFormsControl<>クラスで.NETのコントロールをホストする

続いてMFCのViewクラスのヘッダにCWinFormsControl<>を定義してやります。

protected:
    HICON m_hIcon;
    CWinFormsControl<WindowsFormsControlLibraryForMFC::WinFromGrid> m_grid;

DDXで適当なコントロールを差し替える。

んで、宣言したWindowsFormコントロールを表示するために、DDX_ManagedControlDDXを行う関数の中で呼びます。第二引数はMFC側で定義したダミーのコントロールですね。よくあるパターンでして、StaticTextを使用するのが定石っぽいですね。

void CMFCApplicationDlg::DoDataExchange(CDataExchange* pDX)
{
    CDialog::DoDataExchange(pDX);
    DDX_ManagedControl(pDX, IDC_GRID, m_grid);
}

これで普通に表示できます。 んじゃ、コントロールに対してやりとりするには、というと・・・まぁもう普通のC++/CLIなのでアロー演算子でアクセスしてやればOKです。

void CMFCApplicationDlg::OnBnClickedButton1()
{
    m_grid->AddRow();
}

C#側のイベントを捕まえる

C#側のイベントを捕まえるのも、若干面倒くさいですが、可能です。以下のページを参考にして・・・

まず、OnInitDialogなんかで、以下の様なコードを書きます。

 m_grid->CellValueChanged += MAKE_DELEGATE(System::Windows::Forms::DataGridViewCellValueEventHandler, OnCellValueChanged);

ここでMAKE_DELEGATEの第二引数に当たるのはC++側のイベントハンドラですが、これはヘッダファイル側にこんな感じで定義します。

 // delegate map
    BEGIN_DELEGATE_MAP(CMFCApplicationDlg)
        EVENT_DELEGATE_ENTRY(OnCellValueChanged, System::Object^, System::Windows::Forms::DataGridViewCellValueEventArgs^)
    END_DELEGATE_MAP()

    void OnCellValueChanged(System::Object ^sender, System::Windows::Forms::DataGridViewCellValueEventArgs ^e);

んで、そこで定義されているイベントハンドラにあたるコードを書きます。

void CMFCApplicationDlg::OnCellValueChanged(System::Object ^sender, System::Windows::Forms::DataGridViewCellValueEventArgs ^e)
{
    // 何か実装してね
}

こんな感じで、結構簡単にできます。まぁなんかの時に使えるでしょう。と。

*1:ここでは単に「古いコード」と思ってくださいまし