画面分割とは、ひとつのウィンドウ(Frame)を複数の区画に分け、
別々の情報、あるいは同じ情報の別の側面を表示することです。
この画面分割のためにCSplitterWndクラスがあり、
動的な分割と静的な分割の2種類の使い方があります。
動的な分割は、ひとつの画面(View)を実行中に2つ(またはそれ以上)に分け、
同じ内容の別の部分を表示したりするのに使います。
静的な分割は、あらかじめ画面をいくつかに分けておき、
別々の画面(View)を表示するのに使います。
画面分割では、CFrameWndクラスの中で初期化を行います。
AppWizardで作ったSDIのアプリケーションではCMainFrameクラス、
MDIのアプリケーションではCChildFrameクラスが、
それぞれCFrameWndの派生クラスなので これを使います。
以下では、CChildFrameクラスを例にとって説明します。
ChildFrm.h:
class CChildFrame : public CMDIChildWnd
{
〜
// アトリビュート
protected:
CSplitterWnd m_wndSplitter;
〜
};
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);
}
これにより、CMainFrame又はCChildFrameクラスにm_wndSplitter変数とOnCreateClient関数が定義され、 それらを使って動的な分割のスケルトンが作成されます。
ただしAppWizardでは横2つ,縦2つの分割を行うようなコーディングになっていますので、
個数は自分で変えてやる必要があります。
なお、この分割されたそれぞれの領域をペインと呼びます。
ChildFrm.cpp:
BOOL CChildFrame::OnCreateClient( LPCREATESTRUCT /*lpcs*/,
CCreateContext* pContext)
{
return m_wndSplitter.Create( this,
2, 2, // TODO: 行と列の数を調整してください。
CSize( 10, 10 ), // TODO: 最小の区画サイズを調整してください。
pContext );
}
ここで領域の最大の縦の個数,横の個数を指定します。
ここではCWaterView,CBlueView,CRedView,CGreenViewというViewを例に使います。 これらのViewはClassWizard等でCViewの派生クラスとして作っておきます。
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関数を使って静的な分割の初期化を行います。
これらのViewの中でGetDocument()を呼び出した場合、同一のDocを返します。 このDocは、ドキュメントテンプレート(C〜App::InitInstance()内でAddDocTemplate()している)で 関連付けされたものです。
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);
}
ただし、実際にはウィンドウの枠の幅なども考慮しなければいけないので結構厄介です。
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;
}
ペイン全体で共通のスクロールバーを使用することも出来ます。
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;
}
プログラムの中からスクロールさせる場合、スクロール位置は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)に変更するだけです。