「MFCコントロール - 分割ウインドウ」の版間の差分

提供:MochiuWiki : SUSE, EC, PCB
ナビゲーションに移動 検索に移動
編集の要約なし
159行目: 159行目:
各ペインに描画するには、各ペインを管理しているViewクラスの描画関数(OnDraw関数やOnPaint関数)を用いる。<br>
各ペインに描画するには、各ペインを管理しているViewクラスの描画関数(OnDraw関数やOnPaint関数)を用いる。<br>
<br>
<br>
CPaneTestView.hファイルに以下のソースコードを追加する。<br>
CPaneAView.hファイルとCPaneAView.cppファイルに以下のソースコードを追加する。<br>
  <source lang="c++">
  <source lang="c++">
CPaneAView.h
  #include  "CPaneBView.h"
  #include  "CPaneBView.h"
  #include  "CPaneCView.h"
  #include  "CPaneCView.h"
174行目: 176行目:
  }
  }
  </source>
  </source>
<br>
続いて、CPaneAView.cppファイルに以下のソースコードを追加する。<br>
  <source lang="c++">  
  <source lang="c++">  
CPaneAView.cpp
  #include "CMainFrm.h"
  #include "CMainFrm.h"
   
   

2019年10月29日 (火) 20:46時点における版

概要

分割ウインドウとは、ウインドウが2つ以上に分割されているウィンドウの事である。
Visual C++ではそれぞれの分割された個々のウインドウを「ペイン」と呼ぶ。


左右2分割の分割ウインドウの作成

MFCでは、分割ウインドウを作成するAPIが標準で用意されている。それが、CSplitterWnd::CreateStatic関数である。
下図のような1行2列の2ペインを持つ分割ウインドウのプログラムを記述する。

まず、各ペインを管理するViewクラスを作成する。
Viewクラスは、ペインの数だけ必要になるので、上図のように左右2つに分割すると、Viewクラスは全部で2つ必要になる。
paneA(左ペイン)にはプロジェクト作成時で用意されているViewクラスを利用し、paneB(右ペイン)のViewクラスは新規で作成する。

プロジェクトに新規Viewクラスを追加する。
流れは以下の通りである。
[挿入(I)]->[クラスの新規作成(N)]
  [クラスの種類(T)]   MFCクラスを選択
  [クラス名(N)]     好きなクラス名(ここではCPaneAView)
  [基本クラス(B)]    CFormViewクラス(CViewの派生クラスならどれでもよい)

まず、CMainFrm.hファイルを開いて、以下のコードを入力する。
※MDIアプリケーションの場合は、CChildFrm.hファイルのCChildFrameクラスに同様のコードを追加する。

 CMainFrame.h
 
 class CMainFrame : public CFrameWnd
 {
    // ...
    public:
       CSplitterWnd    m_SplitWndMain;     // スプリッタウィンドウ
    // ...
 }


次に、CMainFrm.cppファイルのCMainFrameクラスにOnCreateClientメンバ関数をオーバーライドする。
手順は以下の通りである。
[表示(V)]->[ClassWizard(W)]
  [プロジェクト(P)]  PaneTest
  [クラス名(N)]    CMainFrame
  [オブジェクトID(I)] CPaneBView
  [メッセージ(G)]   OnCreateClient

そして、CMainFrm.cppファイルに、以下のコードを追加する。

 #include  "CPaneTestDoc.h"  // CPaneAView.hよりも先にインクルード
 #include  "CPaneAView.h"    // CPaneAViewクラスのヘッダをインクルード
 #include  "CPaneBView.h"    // CPaneBViewクラスのヘッダをインクルード
 
 // ...
 
 BOOL CMainFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext) 
 {
    // ...
    // ...
    m_SplitWndMain.CreateStatic(  // スプリッタウィンドウの作成
       this,                      // スプリッタウィンドウの親ウィンドウを指定。ここではメインフレームを分割するのでthisを指定。
       1,                         // 行の分割数を指定。ここでは縦分割は無いので1を指定
       2);                        // 列の分割数を指定。ここでは左右分割なので2を指定
 
    m_SplitWndMain.CreateView(     // paneA(ペイン場所1x1)を作成。
       0,                          // スプリッタの行番号を指定
       0,                          // スプリッタの列番号を指定
       RUNTIME_CLASS(CPaneAView),  // 指定ペインを管理するViewクラスCPaneAViewを指定
       CSize(20, 0),               // ペインのサイズ
       pContext);
 
    m_SplitWndMain.CreateView(     // paneB(ペイン場所1x2)を作成。
       0,                          // スプリッタの行番号を指定
       1,                          // スプリッタの列番号を指定
       RUNTIME_CLASS(CPaneBView),  // paneBを管理するViewクラスCPaneBViewを指定
       CSize(0,0),                 // ペインのサイズ
       pContext);
    // ...
    // ...
 }


ここで注意するのが、CSplitterWnd::CreateView関数の第3引数である。
第3引数において、指定ペインを管理するViewクラスを指定する。
paneAを管理するのはCPaneAViewなのでこれを指定する。同様に、paneBを管理するのはCPaneBViewクラスなので、CPaneBViewを指定する。
尚、MDIアプリケーションの場合も同様に、CChildFrameクラスにOnCreateClient関数をオーバーライドして、OnCreateClient関数内に同様のコードを追加する。


変則分割ペインを持つ分割ウインドウの作成

下図のように、左側に1つ、右側に2つのウインドウを持つ分割ウインドウを作成する。

MFC Pane 02.jpg


CSplitterWnd::CreateStatic関数は、n行m列に分割するしか出来ないので、以下の方法を用いる。
左右に2分割した後、分割した右側のペインを更に上下に2分割する。(このように2つのスプリッタが交錯するような構造をネストと呼ぶ)

まず、基本分割ウインドウと同様に、それぞれのペインを管理するViewクラスを作成する。
ペインAの管理にはCPaneAViewクラス、ペインBはCPaneBView、ペインCはCPaneCViewとしてクラスを追加する。
次に、CMainFrm.hファイルに下記のソースコードを追加する。

 class CMainFrame : public CFrameWnd
 {
    // ...
    public:
       CSplitterWnd  m_SplitMain;  // メインフレームを左右分割する為に使用する分割ウインドウ(メインスプリッタと仮命名)
       CSplitterWnd  m_SplitSub;   // 左右分割されたウインドウの右ペインを上下分割するために使用する分割ウインドウ(サブスプリッタと仮命名)
    // ...
 }


次に、CMainFrm.cppファイルにCMainFrame::OnCreateClient関数をオーバーライドして、下記のソースコードを追加する。
※MDIアプリケーションの場合は、CChildFrameクラスに追加すること。

 #include  "PaneADoc.h"
 #include  "PaneAView.h"  // CPaneAViewクラスのヘッダをインクルード
 #include  "PaneBView.h"  // CPaneBViewクラスのヘッダをインクルード
 #include  "PaneCView.h"  // CPaneCViewクラスのヘッダをインクルード
 
 // ...
 
 BOOL CMainFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext) 
 {
    // ...
    m_SplitMain.CreateStatic(     //  メインスプリッタを作成
       this,                      //  メインスプリッタの親ウインドウを指定。ここではメインフレームを分割するのでthisを指定
       1,                         //  メインスプリッタの行分割数を指定
       2);                        //  メインスプリッタの列分割数を指定

    m_SplitSub.CreateStatic(      // サブスプリッタを作成
       &m_SplitMain,              //   サブスプリッタの親ウィンドウはm_SplitMain
       2,                         //   サブスプリッタの行分割を指定
       1,                         //   サブスプリッタの列分割を指定
       WS_CHILD | WS_VISIBLE,           //   サブスプリッタのスタイルを指定
       m_SplitMain.IdFromRowCol(0,1));  //   子ウィンドウIDを指定。m_SplitMainの行0(=1行目)列1(=2列目)のIDを取得

    m_SplitMain.CreateView(        //  ペインAを作成
       0,                          //  メインスプリッタの行番号を指定
       0,                          //  メインスプリッタの列番号を指定
       RUNTIME_CLASS(CPaneAView),  //  ペインAを管理するViewクラスCPaneAViewを指定。
       CSize(20, 0),               //  ペインのサイズ
       pContext);

    m_SplitSub(                    //  ペインBを作成
       0,                          //  サブスプリッタの行番号を指定
       0,                          //  サブンスプリッタの列番号を指定
       RUNTIME_CLASS(CPaneBView),  //  ペインBを管理するViewクラスCPaneBViewを指定。
       CSize(100, 0),              //  ペインのサイズ
       pContext);

    m_SplitSub(                     //  ペインCを作成。
       1,                           //  サブスプリッタの行番号を指定
       0,                           //  サブンスプリッタの列番号を指定
       RUNTIME_CLASS(CPaneCView),   //  ペインCを管理するViewクラスCPaneCViewを指定。
       CSize(0, 0),                 //  ペインのサイズ
       pContext);
    // ...
 }



管理外Viewクラスからの各ペインへのアクセス方法(SDIの場合)

ここまで、分割ウインドウを作成して様々なペインを作成したが、各ペインをリンクさせながら動作させる方法を説明する。(ペインAで左クリックされたらペインBにテキストを出力する等)
ここでは、上記で作成した変則分割ペインを使用する。

各ペインに描画するには、各ペインを管理しているViewクラスの描画関数(OnDraw関数やOnPaint関数)を用いる。

CPaneAView.hファイルとCPaneAView.cppファイルに以下のソースコードを追加する。

 CPaneAView.h
 
 #include  "CPaneBView.h"
 #include  "CPaneCView.h"
 #include  "CMainFrm.h"

 class CPaneAView : public CFormView
 {
    // ...
    public:
       CPaneBView *pPaneB;  // CPaneBViewクラスへのポインタ
       CPaneCView *pPaneC;  // CPaneCViewクラスへのポインタ
    // ...
 }
 
 CPaneAView.cpp
 
 #include "CMainFrm.h"
 
 // ...
 
 CPaneAView::OnDraw(CDC *pDC)
 {
    // ...
    pPaneB = (CPaneBView*)((CMainFrame*)AfxGetMainWnd())->m_SplitSub.GetPane(0, 0);
    pPaneC = (CPaneCView*)((CMainFrame*)AfxGetMainWnd())->m_SplitSub.GetPane(1, 0);
    // ...
 }

AfxGetMainWnd関数はメインフレームのオブジェクトを返すので、CPaneAtViewクラスの任意の場所でペインBやペインCにアクセス出来る。
また、各ペインのドキュメントには、CWnd::GetDC関数でアクセスできる。例えば、ペインBに"This region is PaneB."と出力する場合は、下記のように記述する。

 pPaneB->GetDC()->TextOut(0, 0, _T("This region is PaneB."));