Tuesday, January 4, 2011

Pane: Creating Dockable Pane

Panes are windows that are typically resizable, tear-off, and dockable. Panes support the following:
  • Microsoft Office-style dialog box bars.
  • Office-style menu bars that support images and docking.
  • Microsoft Outlook-style shortcut bars.
  • Outlook-style caption bars.
  • Outlook-style task panes.
  • Visual Studio-style panes that resemble toolbox, property, and browser panes.
  • Rebar controls whose contents and position persist between runs of an application.
  • Status bars that support icons, animations, and progress bars.
  • A print preview service.
Source from http://msdn.microsoft.com/en-us/library/bb984556.aspx


So, following is how you can create a Dockable Pane using CDockablePane

The Preliminary: Create  a MFC Project. Just for simplicity, you may select the following

  • SDI (Single Document)
  • Document/View Architecture Support
  • Project Style:Visual Studio
  • Visual style & support: Office 2007 (Blue theme)
  • Unticked: Enable visual style switching (thus reduce all the additional code)
  • Use a menu bar and toolbar (with both its option ticked)
  • Advanced frame panes: unticked all (this is 2008 Feature Pack option for wizard auto implementation. This time, we want to do it manually, for learning propose)
  • Keep other as default
  • So the wizard created:
    a. CMyAppApp - The application Class
    b. CMainFrame (Derived from CFrameWnd) - The Main Window Class than contain all windows, toolbar, menu, status bar, scroll bar etc.
    c. CMyAppView- The View Class; a window, the empty area in Main Frame (apart from space taken by toolbar, menu, status bar, scroll bar etc) which programmer use to draw something.
    d. CMyAppDoc - The Document Class, which seemlessly communicate with the view class. The Document class hold all doc data. The View Class draw visually of the Doc data.
    e. CAboutDlg - unimportant dialog class which show this application About.

The Steps
1. Create a class and derived it from CDockablePane. Hint: use VS auto code generation for less work (Project > Add Class...). I name that class as CMyPane.
class CMyPane : public CDockablePane
{
public:
 CMyPane(void);
 ~CMyPane(void);
};


2. Create a object from that class (CMyPane) in the CMainFrame as a protected member.
class CMainFrame : public CFrameWnd
{
 // Other Wizard Generated Code
protected:  // control bar embedded members
 CMyPane m_rightPane
 // Other Wizard Generated Code
};

Hint: Don't forget to include the pane class header in the MainFrm.h e.g. #include "MyPane.h" after the #pragma once



3. 'Create' the Pane visually by supplying the initial data/properties in the CMainFrame::OnCreate().
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{

 // Other Wizard Generated Code
 // Find this code and start coding after that
 m_wndStatusBar.SetIndicators(indicators, sizeof(indicators)/sizeof(UINT));


 if(!m_rightPane.Create(_T("Pane 1"), this, CRect(0, 0, 200, 0), TRUE,
  ID_RIGHT_PANE1, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS |
  WS_CLIPCHILDREN | CBRS_RIGHT | CBRS_FLOAT_MULTI)
 {
  TRACE0("Failed to create Pane 1\n");
  return -1;      // fail to create
 }

 // Other Wizard Generated Code

 m_rightPane.EnableDocking(CBRS_ALIGN_ANY);
 EnableDocking(CBRS_ALIGN_ANY); //Surely already there);
 DockPane(&m_rightPane);

 // Other Wizard Generated Code
 
 return 0;
}

Hint: Don't forget to include the pane class header in the MainFrm.cpp e.g. #include "MyPane.h"
a. in m_rightPane.Create(), only the width data (200) is important in CRect, which will be taken as pane width. The other variable will be ignore.
b. in m_rightPane.Create(), all WS constant is kinda default. on CBRS_RIGHT or CRS_LEFT will put the pane either on right of left respectively.
c. in m_rightPane.EnableDocking(), you can supply other CBRS constant if you want it to only permitted on docking to not all frame side.
d. ID_RIGHT_PANE1 is a ID. You can create it by, going to Resource View, click on String Table folder, right click on the String Table icon, choose Resources Symbol. On Resources Symbol Window, click New. Give a ID name such as ID_RIGHT_PANE1, the value will be given to you automatically. The value in the internal referencen. You don’t need to know what it actual value, just use ID_RIGHT_PANE1 all the way, to refer to this ID.


4. Define CMyPane::OnSize(). Hint: Use VS auto Message code generation. Add Invalidate() in the funtion defination so that the pane will redraw on any pane resize.

void CMyPane::OnSize(UINT nType, int cx, int cy)
{
 CDockablePane::OnSize(nType, cx, cy);

 // TODO: Add your message handler code here
 Invalidate();
}

5. Define CMyPane::OnPaint(). Hint: Use VS auto Message code generation.
Add your own drawing code to give the pane graphical apparent or data representation.


Note:
1. MFC will remember all window setting and size when you debug your application and resize or close the pane. If you alter related code, let say CBRS_RIGHT to CRS_LEFT, you would not see the changes as MFC make sure the registry is remembering that the pane is on right side after the last debug. So you need to have Unregister(). See SetRegistryKey() is irretating during debugging post for how to do it.

2. Usually, CDockablePane would host a MFC Control such as CListCtrl, CTreeControl etc. So we don't paint the control via OnPaint() Message or handle the sizing via OnSize() as we handle it with the control class embedded in the pane. I will cover this in separate post. But, remember the above step if you want to do you own painting without having any control

1 comment: