| 1つのドキュメントを異なる2つのビューで表示する |
1つのドキュメントの内容を2つの異なるビューで表示するためには、新たなドキュメントテンプレートを追加する必要がある。
例として左図のようなMDIアプリケーションを考える。左図では、
スピンボタンコントロールのついたエディットボックスが貼り付けられたフォームビューと、
スライダコントロールの貼り付けられたフォームビューがある。ここでは両方とも0〜100までの数値を表示することができ、
一方のコントロールの値を変更すると、もう一方のコントロールの値もそれに合わせて変更される。
つまり、2つのビューのコントロールは常に同じ値を示すものとする。
まず、プロジェクトを新規作成する。MFC AppWizard(exe)で新規プロジェクト名をMDITest1とする。
アプリケーションの種類はMDIとし、ビュークラスCMDITest1Viewの基本クラスはCFormViewとする。
プロジェクトが作成されたら、フォームにエディットボックスとスピンボタンコントロールを貼り付ける。
スピンボタンコントロールの使用方法については、スピンボタンコントロールの使用を参照すること。
また、エディットボックスにはint型のDDX変数m_iEdit1とCEdit型のDDX変数m_edit1を定義しておくこと。
次に、スライダコントロール用の新しいビュークラスを作成する。
ResourceViewで新しいフォームを作成したら、このフォームに対応したクラスを新規作成する。
クラス名はCMDITest1View2とし、基本クラスはCFormViewを指定する。
クラスを作成したら、フォームにスライダコントロールを貼り付け、int型のDDX変数m_iSlider1と、
CSliderCtrl型のDDX変数m_slider1を定義しておくこと。
ここからが本題になる。新しいビュークラスを作成したら、これを表示させるために新たなドキュメントテンプレートを定義する。
MDITest.cppを開いてCMDITestApp::InitInstance関数の定義部分を見ると、次のような記述が見つかる。
CMultiDocTemplate* pDocTemplate; pDocTemplate = new CMultiDocTemplate( IDR_MDITESTYPE, RUNTIME_CLASS(CMDITestDoc), RUNTIME_CLASS(CChildFrame), // カスタム MDI 子フレーム RUNTIME_CLASS(CMDITestView)); AddDocTemplate(pDocTemplate);CMultiDocTemplateクラスは、MDIのドキュメントテンプレートを定義するクラスである。 上記では、ドキュメントとしてCMDITestDoc、フレームにCChildFrame、ビューにCMDITestViewの各クラスを指定している。 最後のCWinApp::AddDocTemplate関数でアプリケーションのテンプレートリストに作成したテンプレートを追加している。
#include "MDITest1View2.h" (中略) CMultiDocTemplate* pDocTemplate2; pDocTemplate2 = new CMultiDocTemplate( IDR_MDITESTYPE, // ・・・(1) RUNTIME_CLASS(CMDITestDoc), RUNTIME_CLASS(CChildFrame), // カスタム MDI 子フレーム RUNTIME_CLASS(CMDITestView2)); AddDocTemplate(pDocTemplate2);この状態でビルドし、アプリケーションを実行すると、最初に次のようなダイアログが表示されるはずである。
これは複数のドキュメントテンプレートが登録されている場合に表示されるダイアログで、
どのテンプレートで新規作成をおこなうかをユーザに聞いているのである。
ここでは上を選択すればエディットボックスを貼り付けたビューが表示され、
下を選択するとスライダコントロールを貼り付けたビューが表示される。
このダイアログを表示させないようにするために、まずResourceViewのString Tableを開く。
ここにIDR_MDITESTYPEというのがあるので、これをコピー&ペーストしてIDR_MDITESTYPE2を作成する。
IDR_MDITESTYPE2のプロパティから、次のように水色の部分を削除する。
\nMDITes\nMDITes\n\n\nMDITest1.Document\nMDITes Document ↓ \nMDITes\n\n\n\nMDITest1.Document\nMDITes Documentここまでおこなったら、先に記述したドキュメントテンプレートの定義にある(1)の部分を、 IDR_MDITESTYPEからIDR_MDITESTYPE2に変更し、ビルドする。アプリケーションを実行してみると、 ダイアログは表示されずにメインウィンドウが表示され、 エディットボックスが貼り付けられたフォームビューだけが表示されるようになるはずである。
このままではスライダコントロールが貼り付けられたビューが表示されないので、表示させる処理を入れる必要がある。 CMainFrameクラスに、次のような関数を定義してほしい。
void CMainFrame::OpenView(CDocTemplate *pTemplate)
{
CDocument* pDoc = MDIGetActive()->GetActiveDocument();
CMDIChildWnd* pNewFrame = (CMDIChildWnd*)pTemplate->CreateNewFrame(pDoc, NULL);
if( pNewFrame )
pTemplate->InitialUpdateFrame(pNewFrame, pDoc);
}
CMainFrameクラスにOpenView関数を追加したら、CMDITest1App::InitInstance関数の最後にこれを呼び出す処理を記述する。
BOOL CMDITest1App::InitInstance()
{
(中略)
pMainFrame->OpenView(pDocTemplate2);
return TRUE;
}
これで、アプリケーションを起動すると異なる2つのビューが同時に表示されるようになる。
2つのビューが同時に表示できたら、それぞれのビューにあるコントロールの値の同期を取る処理を記述する。
まず2つのビューに共通のドキュメントであるCMDITest1Docクラスに、publicなint型メンバ変数m_iCountを定義する。
次に、ドキュメントクラスのコンストラクタでこの変数を0で初期化しておく。
それぞれのビューは、この変数の値をもとにコントロールの表示処理をおこなうのである。
エディットボックスのあるビューでは、ClassWizardを使ってエディットボックスのEN_CHANGEメッセージのハンドラ関数である OnChangeEdit1関数を定義する。また、CMDITest1View::OnUpdate関数も定義する。それぞれの関数の内容は以下のようにする。
void CMDITest1View::OnChangeEdit1()
{
// TODO: この位置にコントロール通知ハンドラ用のコードを追加してください
if( m_edit1.GetSafeHwnd() == NULL )
return;
UpdateData(TRUE);
((CMDITest1Doc*)GetDocument())->m_iCount = m_iEdit1;
GetDocument()->UpdateAllViews(NULL);
}
void CMDITest1View::OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint)
{
// TODO: この位置に固有の処理を追加するか、または基本クラスを呼び出してください
m_iEdit1 = ((CMDITest1Doc*)GetDocument())->m_iCount;
UpdateData(FALSE);
}
また、スライダコントロールのあるビューでは、ClassWizardを使ってスライダコントロールの
NM_RELEASEDCAPTUREメッセージのハンドラであるOnReleasedcaptureSlider1関数を定義する。
また、CMDITest1View2::OnUpdate関数も定義する。それぞれの関数の内容は以下のようにする。
void CMDITest1View2::OnReleasedcaptureSlider1(NMHDR* pNMHDR, LRESULT* pResult)
{
// TODO: この位置にコントロール通知ハンドラ用のコードを追加してください
if( m_slider1.GetSafeHwnd() == NULL )
return;
UpdateData(TRUE);
((CMDITest1Doc*)GetDocument())->m_iCount = m_iSlider1;
GetDocument()->UpdateAllViews(NULL);
*pResult = 0;
}
void CMDITest1View2::OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint)
{
// TODO: この位置に固有の処理を追加するか、または基本クラスを呼び出してください
m_iSlider1 = ((CMDITest1Doc*)GetDocument())->m_iCount;
UpdateData(FALSE);
}