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

📢 Webサイト閉鎖と移転のお知らせ
このWebサイトは2026年9月に閉鎖いたします。
新しい記事は移転先で追加しております。(旧サイトでは記事を追加しておりません)

文字列「</source>」を「</syntaxhighlight>」に置換
 
(3人の利用者による、間の13版が非表示)
22行目: 22行目:
まず、CMainFrm.hファイルを開いて、以下のコードを入力する。<br>
まず、CMainFrm.hファイルを開いて、以下のコードを入力する。<br>
'''※MDIアプリケーションの場合は、CChildFrm.hファイルのCChildFrameクラスに同様のコードを追加する。'''<br>
'''※MDIアプリケーションの場合は、CChildFrm.hファイルのCChildFrameクラスに同様のコードを追加する。'''<br>
  <source lang="c++">
  <syntaxhighlight lang="c++">
  CMainFrame.h
  CMainFrame.h
   
   
32行目: 32行目:
     // ...
     // ...
  }
  }
  </source>
  </syntaxhighlight>
<br>
<br>
次に、CMainFrm.cppファイルのCMainFrameクラスにOnCreateClientメンバ関数をオーバーライドする。<br>
次に、CMainFrm.cppファイルのCMainFrameクラスにOnCreateClientメンバ関数をオーバーライドする。<br>
手順は以下の通りである。<br>
手順は以下の通りである。<br>
[表示(V)]->[ClassWizard(W)]<br>
[表示(V)]->[ClassWizard(W)]<br>
  [プロジェクト(P)]  PaneTest<br>
  [プロジェクト(P)]  PaneSample<br>
  [クラス名(N)]    CMainFrame<br>
  [クラス名(N)]    CMainFrame<br>
  [オブジェクトID(I)] CPaneBView<br>
  [オブジェクトID(I)] CPaneBView<br>
43行目: 43行目:
<br>
<br>
そして、CMainFrm.cppファイルに、以下のコードを追加する。<br>
そして、CMainFrm.cppファイルに、以下のコードを追加する。<br>
  <source lang="c++">
  <syntaxhighlight lang="c++">
  CMainFrm.cpp
  CMainFrm.cpp
   
   
  #include  "CPaneADoc.h"  // CPaneAView.hよりも先にインクルード
  #include  "CPaneDoc.h"  // CPaneAView.hよりも先にインクルード
  #include  "CPaneAView.h"  // CPaneAViewクラスのヘッダをインクルード
  #include  "CPaneAView.h"  // CPaneAViewクラスのヘッダをインクルード
  #include  "CPaneBView.h"  // CPaneBViewクラスのヘッダをインクルード
  #include  "CPaneBView.h"  // CPaneBViewクラスのヘッダをインクルード
55行目: 55行目:
  {
  {
     // ...
     // ...
     if(FLASE == m_SplitWndMain.CreateStatic(  // スプリッタウインドウの作成
     if(FLASE == m_SplitMain.CreateStatic(  // スプリッタウインドウの作成
                   this,                      // スプリッタウインドウの親ウィンドウを指定
                   this,                      // スプリッタウインドウの親ウィンドウを指定
                                               // ここではメインフレームを分割するのでthisを指定
                                               // ここではメインフレームを分割するのでthisを指定
84行目: 84行目:
     }
     }
     // ...
     // ...
    //return CFrameWndEx::OnCreateClient(lpcs, pContext);
     return TRUE;
     return TRUE;
  }
  }
  </source>
  </syntaxhighlight>
<br>
<br>
ここで注意するのが、CSplitterWndEx::CreateView関数の第3引数である。<br>
ここで注意するのが、CSplitterWndEx::CreateView関数の第3引数である。<br>
108行目: 109行目:
  [クラスの種類(T)]   MFCクラスを選択<br>
  [クラスの種類(T)]   MFCクラスを選択<br>
  [クラス名(N)]     CPaneBViewとCPaneCView<br>
  [クラス名(N)]     CPaneBViewとCPaneCView<br>
  [基本クラス(B)]    CFormViewクラス(CViewの派生クラスならどれでもよい)<br>
  [基本クラス(B)]    CViewの派生クラス(ここではCFormViewクラスを使用する)<br>
<br>
<br>
次に、CMainFrm.hファイルに下記のソースコードを追加する。<br>
次に、CMainFrm.hファイルに下記のソースコードを追加する。<br>
  <source lang="c++">
  <syntaxhighlight lang="c++">
  CMainFrm.h
  CMainFrm.h
   
   
  class CMainFrame : public CFrameWnd
  class CMainFrame : public CFrameWndEx
  {
  {
     // ...
     // ...
     public:
     public:
       CSplitterWnd  m_SplitMain;  // メインフレームを左右分割する為に使用する分割ウインドウ(メインスプリッタと仮命名)
       CSplitterWnd  m_SplitMain;  // メインフレームを左右分割する為に使用する分割ウインドウ(メインスプリッタと命名)
       CSplitterWnd  m_SplitSub;  // 左右分割されたウインドウの右ペインを上下分割するために使用する分割ウインドウ(サブスプリッタと仮命名)
       CSplitterWnd  m_SplitSub;  // 左右分割されたウインドウの右ペインを上下分割するために使用する分割ウインドウ(サブスプリッタと命名)
     // ...
     // ...
  }
  }
  </source>
  </syntaxhighlight>
<br>
<br>
次に、CMainFrm.cppファイルにCMainFrame::OnCreateClient関数をオーバーライドして、下記のソースコードを追加する。<br>
次に、CMainFrm.cppファイルにCMainFrame::OnCreateClient関数をオーバーライドして、下記のソースコードを追加する。<br>
手順は以下の通りである。<br>
手順は以下の通りである。<br>
[表示(V)]->[ClassWizard(W)]<br>
[表示(V)]->[ClassWizard(W)]<br>
  [プロジェクト(P)]  PaneTest<br>
  [プロジェクト(P)]  PaneSample<br>
  [クラス名(N)]    CMainFrame<br>
  [クラス名(N)]    CMainFrame<br>
  [オブジェクトID(I)] CPaneBViewとCPaneCView<br>
  [オブジェクトID(I)] CPaneBViewとCPaneCView<br>
133行目: 134行目:
<br>
<br>
'''※MDIアプリケーションの場合は、CChildFrameクラスに追加すること。'''<br>
'''※MDIアプリケーションの場合は、CChildFrameクラスに追加すること。'''<br>
  <source lang="c++">
  <syntaxhighlight lang="c++">
  CMainFrm.cpp
  CMainFrm.cpp
   
   
  #include  "CPaneADoc.h"
  #include  "CPaneDoc.h"
  #include  "CPaneAView.h"  // CPaneAViewクラスのヘッダをインクルード
  #include  "CPaneAView.h"  // CPaneAViewクラスのヘッダをインクルード
  #include  "CPaneBView.h"  // CPaneBViewクラスのヘッダをインクルード
  #include  "CPaneBView.h"  // CPaneBViewクラスのヘッダをインクルード
146行目: 147行目:
  {
  {
     // ...
     // ...
     m_SplitMain.CreateStatic(    //  メインスプリッタを作成
     if(FALSE == m_SplitMain.CreateStatic(    //  メインスプリッタを作成
      this,                      //  メインスプリッタの親ウインドウを指定。ここではメインフレームを分割するのでthisを指定
                  this,                      //  メインスプリッタの親ウインドウを指定。ここではメインフレームを分割するのでthisを指定
      1,                        //  メインスプリッタの行分割数を指定
                  1,                        //  メインスプリッタの行分割数を指定
      2);                       //  メインスプリッタの列分割数を指定
                  2))                       //  メインスプリッタの列分割数を指定
 
    {
     m_SplitSub.CreateStatic(      // サブスプリッタを作成
      return FALSE;
      &m_SplitMain,              //  サブスプリッタの親ウィンドウはm_SplitMain
    }
      2,                        //  サブスプリッタの行分割を指定
      1,                        //  サブスプリッタの列分割を指定
     if(FALSE == m_SplitSub.CreateStatic(      // サブスプリッタを作成
      WS_CHILD | WS_VISIBLE,           //  サブスプリッタのスタイルを指定
                  &m_SplitMain,              //  サブスプリッタの親ウィンドウはm_SplitMain
      m_SplitMain.IdFromRowCol(0,1)); //  子ウィンドウIDを指定。m_SplitMainの行0(=1行目)列1(=2列目)のIDを取得
                  2,                        //  サブスプリッタの行分割を指定
 
                  1,                        //  サブスプリッタの列分割を指定
     m_SplitMain.CreateView(        //  ペインAを作成
                  WS_CHILD | WS_VISIBLE,           //  サブスプリッタのスタイルを指定
      0,                          //  メインスプリッタの行番号を指定
                  m_SplitMain.IdFromRowCol(0, 1))) //  子ウィンドウIDを指定。m_SplitMainの行0(=1行目)列1(=2列目)のIDを取得
      0,                          //  メインスプリッタの列番号を指定
    {
      RUNTIME_CLASS(CPaneAView),  //  ペインAを管理するViewクラスCPaneAViewを指定。
      return FALSE;
      CSize(20, 0),               //  ペインのサイズ
     }
      pContext);
 
    if(FALSE == m_SplitMain.CreateView(        //  ペインAを作成
     m_SplitSub(                    //  ペインBを作成
                  0,                          //  メインスプリッタの行番号を指定
      0,                          //  サブスプリッタの行番号を指定
                  0,                          //  メインスプリッタの列番号を指定
      0,                          //  サブンスプリッタの列番号を指定
                  RUNTIME_CLASS(CPaneAView),  //  ペインAを管理するViewクラスCPaneAViewを指定。
      RUNTIME_CLASS(CPaneBView),  //  ペインBを管理するViewクラスCPaneBViewを指定。
                  CSize(320, 240),           //  ペインのサイズ
      CSize(100, 0),             //  ペインのサイズ
                  pContext))
      pContext);
    {
 
      return FALSE;
     m_SplitSub(                    //  ペインCを作成。
    }
      1,                          //  サブスプリッタの行番号を指定
      0,                          //  サブンスプリッタの列番号を指定
     if(FALSE == m_SplitSub(                    //  ペインBを作成
      RUNTIME_CLASS(CPaneCView),  //  ペインCを管理するViewクラスCPaneCViewを指定。
                  0,                          //  サブスプリッタの行番号を指定
      CSize(0, 0),                 //  ペインのサイズ
                  0,                          //  サブンスプリッタの列番号を指定
      pContext);
                  RUNTIME_CLASS(CPaneBView),  //  ペインBを管理するViewクラスCPaneBViewを指定。
                  CSize(320, 120),           //  ペインのサイズ
                  pContext))
    {
      return FALSE;
    }
     if(FALSE == m_SplitSub(                    //  ペインCを作成。
                  1,                          //  サブスプリッタの行番号を指定
                  0,                          //  サブスプリッタの列番号を指定
                  RUNTIME_CLASS(CPaneCView),  //  ペインCを管理するViewクラスCPaneCViewを指定。
                  CSize(320, 120),             //  ペインのサイズ
                  pContext))
    {
      return FALSE;
    }
     // ...
     // ...
    //return CFrameWndEx::OnCreateClient(lpcs, pContext);
    return TRUE;
  }
  }
  </source>
  </syntaxhighlight>
<br><br>
<br><br>


== 管理外Viewクラスからの各ペインへのアクセス方法(SDIの場合) ==
== 管理外Viewクラスからの各ペインへのアクセス方法(SDIの場合) ==
ここまで、分割ウインドウを作成して様々なペインを作成したが、各ペインをリンクさせながら動作させる方法を説明する。(ペインAで左クリックされたらペインBにテキストを出力する等)<br>
ここまで、分割ウインドウを作成して様々なペインを作成したが、各ペインをリンクさせながら動作させる方法を説明する。<br>
(ペインAで左クリックされたらペインBにテキストを出力する等)<br>
ここでは、上記で作成した変則分割ペインを使用する。<br>
ここでは、上記で作成した変則分割ペインを使用する。<br>
<br>
<br>
190行目: 209行目:
<br>
<br>
CPaneAView.hファイルとCPaneAView.cppファイルに以下のソースコードを追加する。<br>
CPaneAView.hファイルとCPaneAView.cppファイルに以下のソースコードを追加する。<br>
  <source lang="c++">
  <syntaxhighlight lang="c++">
  CPaneAView.h
  CPaneAView.h
   
   
205行目: 224行目:
     // ...
     // ...
  }
  }
  </source>
  </syntaxhighlight>
  <source lang="c++">  
  <syntaxhighlight lang="c++">  
  CPaneAView.cpp
  CPaneAView.cpp
   
   
220行目: 239行目:
     // ...
     // ...
  }
  }
  </source>
  </syntaxhighlight>
AfxGetMainWnd関数はメインフレームのオブジェクトを返すので、CPaneAtViewクラスの任意の場所でペインBやペインCにアクセス出来る。<br>
AfxGetMainWnd関数はメインフレームのオブジェクトを返すので、CPaneAtViewクラスの任意の場所でペインBやペインCにアクセス出来る。<br>
また、各ペインのドキュメントには、CWnd::GetDC関数でアクセスできる。例えば、ペインBに"This region is PaneB."と出力する場合は、下記のように記述する。<br>
また、各ペインのドキュメントには、CWnd::GetDC関数でアクセスできる。<br>
  <source lang="c++">
例えば、ペインBに"This region is PaneB."と出力する場合は、下記のように記述する。<br>
  <syntaxhighlight lang="c++">
CPaneAView.cpp
  pPaneB->GetDC()->TextOut(0, 0, _T("This region is PaneB."));
  pPaneB->GetDC()->TextOut(0, 0, _T("This region is PaneB."));
  </source>
  </syntaxhighlight>
<br><br>
 
== 分割ウインドウの固定化 ==
分割ウインドウを手動で変更できないようにする方法がある。(分割を固定にする)<br>
<br>
まず、CSplitterWndExの派生クラスを作成して、OnLButtonDownハンドラをオーバーライドして<br>
CSplitterWndEx::OnLButtonDown関数を呼ばないようにすれば可能である。<br>
また、スプリッターバー上でマウスカーソルを変更しないようにするには、OnMouseMoveハンドラをオーバーライドして<br>
CSplitterWndEx::OnMouseMove関数を呼ばないようにすれば可能である。<br>
<br>
上記よりも簡単な方法がある。<br>
それは、CSplitterWndExの派生クラスを作成して、OnNcHitTestハンドラをオーバーライドして<br>
CSplitterWndEx::OnNcHitTest関数で無条件にHTBORDERを返せば、マウスカーソルも変更されない。<br>
<br>
<br>
分割ウインドウにて、仕切り位置を固定し、ユーザーに位置変更をさせない方法がある。<br>
また、カーソルが乗っても移動用のカーソルに変更しないようにする。<br>
<br>
まず、CSpritterWndまたはCSpritterWndExの派生クラスを作成する。(ここではCMySplitterWndEx)<br>
CMySplitterWndExにてHitTestハンドラをオーバーライドする。<br>
HitTestハンドラは、下記のように宣言されている。<br>
virtual int HitTest(CPoint pt) const;
<br>
HitTestの戻り値は下記の通りで、操作禁止にしたい方向のときに"0"を返すようにする。<br>
<syntaxhighlight lang="c++">
enum HitTestValue
{
    noHit                  = 0,
    vSplitterBox            = 1,
    hSplitterBox            = 2,
    bothSplitterBox        = 3,          // just for keyboard
    vSplitterBar1          = 101,        // 横方向の上下に動かすバー
    vSplitterBar15          = 115,
    hSplitterBar1          = 201,        // 縦方向の左右に動かすバー
    hSplitterBar15          = 215,
    splitterIntersection1  = 301,        // 縦と横が交わる点
    splitterIntersection225 = 525
};
</syntaxhighlight>
<syntaxhighlight lang="c++">
int CMySplitterWndEx::HitTest(CPoint pt) const
{
    int ht = CSplitterWndEx::HitTest(pt);  // CSpritterWndExは、テーマの変更処理が追加されただけなのでCSplitterWndでも可
    if(ht == 201 || ht == 301)
    {  // 横方向のバーだけ上下に移動可
      ht = 0;  // ヒットしてないことにする
    }
    return ht;
}
</syntaxhighlight>
上記の例では、横方向のバーだけ上下に移動出来るようになり、縦方向や交差している点ではカーソルも変更されない。<br>
enumのHitTestValueはcppファイル内で宣言されているため、使用する場合は各自で追加する。<br>
<br><br>
 
== リサイズ変更 ==
SetRowInfo and SetColumnInfo are responsible for managing the size of the splitters. Add a handler for the WM_ONSIZE message and add the following code:<br>
<br>
Hide  Copy Code<br>
void CMainFrame::OnSize(UINT nType, int cx, int cy)
{
    CFrameWnd::OnSize(nType, cx, cy);
    CRect cr;
    GetWindowRect(&cr);
 
    if (  m_bInitSplitter && nType != SIZE_MINIMIZED )
    {
        m_mainSplitter.SetRowInfo( 0, cy, 0 );
        m_mainSplitter.SetColumnInfo( 0, cr.Width() / 2, 50);
        m_mainSplitter.SetColumnInfo( 1, cr.Width() / 2, 50);
 
        m_mainSplitter.RecalcLayout();
    }
}
<br>
In the above code, we first check that the splitters have been initialized by checking the boolean value. This check is required since I think a WM_SIZE message is passed to the frame before the create method is ran - therefore, the object won't exist the first time this code runs and would crash if you didn't check for its existence.<br>
<br>
The application should now run and provide something resembling the screenshot at the start of the article! I will probably add a second article in this series to add further nested splitters and other views based on forms! Stay tuned...<br>
<br><br>
<br><br>


__FORCETOC__
__FORCETOC__
[[カテゴリ:MFC]]
[[カテゴリ:MFC]]