S-JIS[1999-11-17/2003-09-07]

画面分割(CSplitterWnd)の使用方法

画面分割とは、ひとつのウィンドウ(Frame)を複数の区画に分け、 別々の情報、あるいは同じ情報の別の側面を表示することです。
この画面分割のためにCSplitterWndクラスがあり、 動的な分割静的な分割の2種類の使い方があります。

動的な分割は、ひとつの画面(View)を実行中に2つ(またはそれ以上)に分け、 同じ内容の別の部分を表示したりするのに使います。

動的な分割前   ←→   動的な分割後

静的な分割は、あらかじめ画面をいくつかに分けておき、 別々の画面(View)を表示するのに使います。

静的な分割


分割のコーディング

動的な分割でも静的な分割でも、作成する手順はだいたい同じで、初期化の関数が違うだけです。

画面分割では、CFrameWndクラスの中で初期化を行います。
AppWizardで作ったSDIのアプリケーションではCMainFrameクラス、 MDIのアプリケーションではCChildFrameクラスが、 それぞれCFrameWndの派生クラスなので これを使います。
以下では、CChildFrameクラスを例にとって説明します。


  1. CSplitterWndクラスの変数を定義します。

    ChildFrm.h:

    class CChildFrame : public CMDIChildWnd
    {
    〜
    // アトリビュート
    protected:
    	CSplitterWnd m_wndSplitter;
    〜
    };
    

  2. OnCreateClientメンバー関数をオーバーライドします。
    CSplitterWndの初期化は、OnCreateClient関数の中で行います。

    ChildFrm.h:

    class CChildFrame : public CMDIChildWnd
    {
    〜
    //オーバーライド
    	// ClassWizard は仮想関数を生成しオーバーライドします。
    	//{{AFX_VIRTUAL(CChildFrame)
    	public:
    	virtual BOOL OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext);
    	//}}AFX_VIRTUAL
    〜
    };
    
    ChildFrm.cpp:
    BOOL CChildFrame::OnCreateClient( LPCREATESTRUCT /*lpcs*/,
    	CCreateContext* pContext)
    {
    	// TODO: この位置に固有の処理を追加するか、または基本クラスを呼び出してください
    
    	//動的な分割の例
    	return m_wndSplitter.Create(this,2,2,CSize(10,10),pContext);
    //	return CMDIChildWnd::OnCreateClient(lpcs, pContext);
    }
    

  3. ここまでの作業ですが、最初から画面分割をしようと思っている場合、 AppWizardの途中の「高度な設定」で画面分割を指定することが出来ます。(VC++4.0)

    「高度な設定」ボタン「ウィンドウスタイル」タブ「ウィンドウ分割を使用(W)」チェック

    これにより、CMainFrame又はCChildFrameクラスにm_wndSplitter変数とOnCreateClient関数が定義され、 それらを使って動的な分割のスケルトンが作成されます。

    ただしAppWizardでは横2つ,縦2つの分割を行うようなコーディングになっていますので、 個数は自分で変えてやる必要があります。
    なお、この分割されたそれぞれの領域をペインと呼びます。


動的な画面分割のコーディング


  1. OnCreateClientメンバー関数の中で動的な分割のための初期化を行います。

    ChildFrm.cpp:

    BOOL CChildFrame::OnCreateClient( LPCREATESTRUCT /*lpcs*/,
    	CCreateContext* pContext)
    {
    	return m_wndSplitter.Create( this,
    		2, 2,                 // TODO: 行と列の数を調整してください。
    		CSize( 10, 10 ),      // TODO: 最小の区画サイズを調整してください。
    		pContext );
    }
    
    ここで領域の最大の縦の個数,横の個数を指定します。
    これで実行すると、指定した個数の画面に分割することが出来るようになります。
    もちろん、画面に表示する内容はCView派生クラスのOnDrawで描いてくださいね。
  2. 動的な分割は、これで全てです!
    実行中に分割が行われた場合、現在表示中のCViewの派生クラスがペイン毎に1つずつ作られます。 これらのViewに対するDocは、同一の実体が使用されます。 そのため、同一内容の情報を複数の状態で参照することが可能になるのです。


静的な画面分割のコーディング


  1. 分割された領域を表示するためのViewを作成します。

    ここではCWaterView,CBlueView,CRedView,CGreenViewというViewを例に使います。 これらのViewはClassWizard等でCViewの派生クラスとして作っておきます。


  2. OnCreateClientメンバー関数の中で静的な分割のための初期化を行います。

    ChildFrm.cpp:

    
    #include "WaterView.h"	//別途作っておく
    #include "BlueView.h"
    #include "RedView.h"
    #include "GreenView.h"
    〜
    BOOL CChildFrame::OnCreateClient( LPCREATESTRUCT /*lpcs*/,
    	CCreateContext* pContext)
    {
    	if(!m_wndSplitter.CreateStatic( this,
    		2, 2,                 // TODO: 行と列の数を調整してください。
    		WS_CHILD | WS_VISIBLE )
    	) return FALSE;
    
    	if(!m_wndSplitter.CreateView(0,0,RUNTIME_CLASS(CWaterView),CSize(128,64),pContext)) return FALSE;
    	if(!m_wndSplitter.CreateView(0,1,RUNTIME_CLASS(CBlueView) ,CSize( 96,64),pContext)) return FALSE;
    	if(!m_wndSplitter.CreateView(1,0,RUNTIME_CLASS(CRedView)  ,CSize(128,32),pContext)) return FALSE;
    	if(!m_wndSplitter.CreateView(1,1,RUNTIME_CLASS(CGreenView),CSize( 96,32),pContext)) return FALSE;
    
    	return TRUE;
    }
    
    まずCreateStatic関数を使って静的な分割の初期化を行います。
    そして、全てのペインにCreateView()を使ってViewを割り当てます。

    これらのViewの中でGetDocument()を呼び出した場合、同一のDocを返します。 このDocは、ドキュメントテンプレート(C〜App::InitInstance()内でAddDocTemplate()している)で 関連付けされたものです。


  3. 領域全体のサイズ

    CreateViewでそれぞれの領域のサイズを指定することが出来ます。
    が、必ずしもCreateViewで指定した大きさにはなりません。 これは、全体の大きさは別のところで決められてしまうためです。

    ChildFrm.cpp:

    BOOL CChildFrame::PreCreateWindow(CREATESTRUCT& cs)
    {
    	// TODO: この位置で CREATESTRUCT cs の設定を行って、Window クラスまたは
    	//       スタイルを変更してください。
    
    	cs.cx=128+96;	//全体の幅
    	cs.cy= 64+32;	//全体の高さ
    
    	return CMDIChildWnd::PreCreateWindow(cs);
    }
    
    ただし、実際にはウィンドウの枠の幅なども考慮しなければいけないので結構厄介です。


  4. 独立スクロールバー

    CreateViewで指定するViewにCScrollViewの派生クラスを使えば、 それぞれのペインに対して独立のスクロールが出来ます。


    この例では、上と下のペインで別々に横スクロールを行うことが出来ます。

    なお、普通のCScrollViewでは、クライアント領域のサイズがSetScrollSizes()で指定したサイズ(大抵はOnInitialUpdate()の中で呼び出している)より小さくならないと スクロールバーは表示されません。[2003-09-07]

    ChildFrm.cpp:
    BOOL CChildFrame::OnCreateClient( LPCREATESTRUCT /*lpcs*/,
    	CCreateContext* pContext)
    {
    	if(!m_wndSplitter.CreateStatic( this,
    		2, 1,                 // TODO: 行と列の数を調整してください。
    		WS_CHILD | WS_VISIBLE)
    	) return FALSE;
    
    	if(!m_wndSplitter.CreateView(0,0,RUNTIME_CLASS(CScr1View),CSize(128,64),pContext)) return FALSE;
    	if(!m_wndSplitter.CreateView(1,0,RUNTIME_CLASS(CScr2View),CSize( 96,64),pContext)) return FALSE;
    
    	return TRUE;
    }
    

  5. 共有スクロールバー

    ペイン全体で共通のスクロールバーを使用することも出来ます。


    この例では、下のスクロールバーを動かすことにより双方のペインがスクロールします。
    ChildFrm.cpp:
    BOOL CChildFrame::OnCreateClient( LPCREATESTRUCT /*lpcs*/,
    	CCreateContext* pContext)
    {
    	if(!m_wndSplitter.CreateStatic( this,
    		2, 1,                 // TODO: 行と列の数を調整してください。
    		WS_CHILD | WS_VISIBLE
    		| WS_HSCROLL	//水平スクロールバーを共有
    		| WS_VSCROLL	//垂直スクロールバーを共有
    		)
    	) return FALSE;
    
    	if(!m_wndSplitter.CreateView(0,0,RUNTIME_CLASS(CScr1View),CSize(128,64),pContext)) return FALSE;
    	if(!m_wndSplitter.CreateView(1,0,RUNTIME_CLASS(CScr2View),CSize( 96,64),pContext)) return FALSE;
    
    	return TRUE;
    }
    

  6. プログラム内で共有スクロールする場合

    プログラムの中からスクロールさせる場合、スクロール位置は1つのペインに対してのみ セットしてやれば充分ですが、再描画はスクロールする全てのペインに指示する必要があります。
    以下は、横スクロールさせるサブルーチンの例です。

    ChildFrm.h:

    class CChildFrame : public CMDIChildWnd
    {
    〜
    	void SetPosX(int dx);	//dxの位置にスクロールさせる
    〜
    };
    
    
    ChildFrm.cpp:
    void CChildFrame::SetPosX(int dx)
    {
    	CView *pView1=(CView*)m_wndSplitter.GetPane(0,0);
    	CView *pView2=(CView*)m_wndSplitter.GetPane(1,0);
    	ASSERT(pView1);
    	ASSERT(pView2);
    
    	int sx=pView1->GetScrollPos(SB_HORZ);
    	if(dx!=sx){
    		pView1->SetScrollPos(SB_HORZ,dx);	//スクロール後のX座標を指定
    		pView1->ScrollWindow(sx-dx,0); //全てのペインをスクロール
    		pView2->ScrollWindow(sx-dx,0); //
    	}
    }
    
    GetPane()は、ペインを描画しているViewを取得するための関数です。

    縦スクロールの場合は、xをyに、SB_HORZをSB_VERTに変え、 ScrollWindow(0,sy-dy)に変更するだけです。


VC++ページへ戻る / 技術メモへ戻る
メールの送信先:ひしだま