Sunday, January 9, 2011

Creating a List Control in a Dockable Pane

CMFCListCtrl is a latest version of CListCtrl.

Let see how to create a CMFCListCtrl in a CDockablePane, or actually can also be implemented in most CWnd such as CMainFrame.

The Steps
1. Let say I have a class name CNodePane derived from CDockablePane.

2. The CMainFrame have a member of CNodePane m_nodePane.

3. CNodePane has readily incorporate all necessary functions needed as shown in the previous post; Pane: Creating Dockable Pane



The Steps
1. Add a member to CNodePane, namely CListCtrl m_nodeList. This is also what people call subclass CListCtrl into CNodePane.
#pragma once
#include "afxdockablepane.h"

class CNodePane :
 public CDockablePane
{
public:
 CNodePane(void);
 ~CNodePane(void);
protected:
 CMFCListCtrl m_nodeList; // Add This
public:
 DECLARE_MESSAGE_MAP()
};



2. Write CNodePane::OnCreate(). In this function which will be call by Framework when a object of CNodePane (in this case m_nodePane) is created. Take this opportunity to create the m_nodeList window. Hint: use VS auto message code generator, find WM_CREATE message.
int CNodePane::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
 if (CDockablePane::OnCreate(lpCreateStruct) == -1)
  return -1;
 
 // TODO:  Add your specialized creation code here
 CRect rect;
 GetClientRect(&rect);
 
 if(!m_nodeList.Create(WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_VSCROLL 
  | LVS_REPORT, rect, this, ID_NODE_LIST))
 {
  TRACE0("Failed to create Nodes List\n");
  return -1;      
 }


 m_nodeList.SendMessage(LVM_SETEXTENDEDLISTVIEWSTYLE, 0, LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES);
 return 0;
}




3. Write CNodePane::OnSize() to handle the change of size of m_nodeList window when m_nodePane window is resize. Actually, we use this function to give actual size of m_nodeList window. Although m_nodePane.Create() required a data of CRect as the m_nodePane window size, we usually give a dump data, such as CRect(0,0,0,0). The actual size will be supply during CNodePane::OnSize() via m_nodeList.SetWindowPos()
void CNodePane::OnSize(UINT nType, int cx, int cy)
{
 CDockablePane::OnSize(nType, cx, cy);

 // TODO: Add your message handler code here
 CRect rect;
 GetClientRect(&rect);

 m_nodeList.SetWindowPos(NULL, rect.left+50, rect.top+50, rect.Width()-60, rect.Height()-60, SWP_NOACTIVATE | SWP_NOZORDER); 
 // as you can see my m_nodeList is not fully fill the m_nodePane Window  Client Area 
}



4. If you fill all m_nodePane window Client Area with m_nodeList window, then your CListCtrl is ready to handle data manipulation.



5. However if m_nodeList window only occupy partial of m_nodePane window, you need to handle CNodePane::OnEraseBkgnd() which is filling the background by repainting back.
BOOL CNodePane::OnEraseBkgnd(CDC* pDC)
{
 // TODO: Add your message handler code here and/or call default
 CRect rt;
 GetClientRect(&rt);
 
 CBrush br;
 br.CreateStockObject(WHITE_BRUSH);
 pDC->SelectObject(&br);

 pDC->Rectangle(&rt); // I just fill all the BG with white brush

 return CDockablePane::OnEraseBkgnd(pDC);
}



6. You also need to call Invalidate() in last line of CNodePane::OnSize() to call the necessary function including CNodePane::OnPaint() and CNodePane::OnEraseBkgnd().
void CNodePane::OnSize(UINT nType, int cx, int cy)
{
 CDockablePane::OnSize(nType, cx, cy);

 // TODO: Add your message handler code here
 CRect rect;
 GetClientRect(&rect);

 m_nodeList.SetWindowPos(NULL, rect.left+50, rect.top+50, rect.Width()-60, 
  rect.Height()-60, SWP_NOACTIVATE | SWP_NOZORDER);

 Invalidate(); //Add this
}



Note:
1. As NodePane.cpp may use ID resource when you call m_nodeList.Create(), don’t forget to #include “resource.h” or you will have the following error
Error 1 error C2065: 'ID_NODE_LIST' : undeclared identifier

Wednesday, January 5, 2011

SetRegistryKey() is irretating during debugging

If you are writing your application, and you are run & test, you want to see the change on every code variation.

However, if you have SetRegistryKey() running, your app will start the app with the last setting save in the registry. This include Windows position & size etc.

However, you can delete the function couse you will have assert error. So how to do it?

By using Unregister(). SetRegistry() can be found in App::InitInstance(). See following.

BOOL CFEAApp::InitInstance()
{
 //Other Code
 AfxEnableControlContainer();
 SetRegistryKey(_T("aaa"));
 LoadStdProfileSettings(4);  // Load standard INI file options (including MRU)
 
 //Just add the following unregister() after LoadStfProfileSetting()
 Unregister();

 //Other Code
 return TRUE;
}

Tuesday, January 4, 2011

Pane: Creating Outlook Bar Tab Style

Can be view at http://msdn.microsoft.com/en-us/library/cc309031(v=VS.90).aspx

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

Monday, January 3, 2011

How we connect the Document class, View class & the Frame Window class

Document templates serve as the connection between documents, frame windows and views. Document templates class is define in the Application Class

CSingleDocTemplate* pDocTemplate;
pDocTemplate = new CSingleDocTemplate(
IDR_MAINFRAME,
RUNTIME_CLASS(CSetPaneSizeDoc),
RUNTIME_CLASS(CMainFrame),       // main SDI frame window
RUNTIME_CLASS(CSetPaneSizeView));
AddDocTemplate(pDocTemplate);


Code explaination:
CSetPaneSizeDoc derived from CDocument.
CSetPaneSizeView derived from CView.
CMainFrame derived from CFrameWnd.


Post Script:
For this dedicated tutorial, the full derivation is, which we interest (just for fun info)
CSetPaneSizeDoc: CDocument: CCmdTarget: CObject
CSetPaneSizeView: CFormView: CScrollView: CView:CWnd: CCmdTarget: CObject
CMainFrame :CFrameWndEx:CFrameWnd: :CWnd: CCmdTarget: CObject

Sunday, January 2, 2011

NewCo: The UI Concept





Hey,

Read up a lot on CDockablePane and manage to sketch the main User Interface for my project.

Search for a bunch of example on CDockablePane. Found one that maybe help me. I print the source code and ready to burn late oil tonight, hopely I can understand the concept and continue with the app development.

Hello World!

Hi,

It is 2am now in where I'm staying. Today was the day that I say to myself that I should fulfill my words. I was always thinking on finishing my Window Programming learning. Win32 API, MFC and others always stack on my mind, saying that i can do more with that skill stuck into my brain.

But I found myself lack of time. Actually, lack of discipline, and courage to make the time.

Well today, I clean my small room in my 8th level apartment, got some space with a working table. Not much, but I will consider this is my "Campus'. I will stuck here, everyday after I spend time with my wife and release my work fatigue.

What with this blog? It will be my note, where i post lesson i learned. I post some concept draft where I like to achieve during my training and how i approach it. it will be C++, calling Win32 API via MFC class library. And this time, stop with high ambition. That is why, i always stop in the middle. Over ambition never too good. It kill the dream and you too frustrating when learning the basic things is hard enough.







So i found that I can use OpenGL easily with MFC. No biggie than. I will need a drawing library for 3D graphic. GDI and GDI+ could not be used for 3D. DirectX seem to hard for me.

Anyway, I need to draw the app concept first. That would be on tomorrow.