Sudoku, a complete MFC application.  Part 4

AndyAinscowFreelance programmer / Consultant
CERTIFIED EXPERT
Published:

Introduction:

Displaying information on the statusbar.  

Continuing from the third article about sudoku.  

Open the project in visual studio.

Status bar – let’s display the timestamp there.  We need to get the timestamp from the document so we add one public function there (after the GetGame).  Open SudokuDoc.h and append a new line:

    char* GetGame() { return m_arGame; };
                          const CString& GetInfo() { return m_szInfo; };

Open in new window


The view is notified when information is changed so we can use the handler (OnUpdate) to also handle this information.  If one looks in the code in the CMainFrame class then one sees that the statusbar is a child of that window, nothing to do with the view – so we will delegate the actual display of information to the frame window itself.

In the SudokuView.cpp file modify it to be as follows (adding the #include “MainFrm.h” after the existing #include “SudokuView.h”)

#include "SudokuView.h"
                      #include "MainFrm.h"

Open in new window


Next we modify the OnUpdate function, adding a new line directly after the case statement

    case CSudokuDoc::eLoadGame:
                              //Update the status bar on the frame
                              static_cast<CMainFrame*>(GetParentFrame())->DisplayStatusInfo(GetDocument()->GetInfo());

Open in new window


The GetParentFrame returns a generic CFrameWnd pointer, we cast this to our specific CMainFrame class which is a derived class from CFrameWnd.  Notice this has a new function – DisplayStatusInfo – which we must now add to the CMainFrame class.  Modify the MainFrm.h file so it is as follows:

public:
                          afx_msg void OnClose();
                          void DisplayStatusInfo(const CString& szInfo);

Open in new window


Now in the MainFrm.cpp file add at the end of the file the function body:

void CMainFrame::DisplayStatusInfo(const CString& szInfo)
                      {
                      }

Open in new window


The program should compile but the status bar needs a little work now.  In the OnCreate function of the CMainFrame there should be the following code:

    if (!m_wndStatusBar.Create(this) ||
                              !m_wndStatusBar.SetIndicators(indicators,
                                sizeof(indicators)/sizeof(UINT)))
                          {
                              TRACE0("Failed to create status bar\n");
                              return -1;      // fail to create
                          }

Open in new window


This creates a default status bar.  We want a custom one which will display menu prompts as the default one does and also have two panes, one for the hint (later article) and one for the time stamp.

We also want some extra functionality so we will create a class derived from CStatusBar (NOT CStatusBarCtrl – careful when selecting the base class).  I have explained in step 3 how to create a CGridButton – we will do similar to create a CSudokuStatusBar.

You should have the following:

class CSudokuStatusBar : public CStatusBar
                      {
                          DECLARE_DYNAMIC(CSudokuStatusBar)
                      
                      public:
                          CSudokuStatusBar();
                          virtual ~CSudokuStatusBar();
                      
                      protected:
                          DECLARE_MESSAGE_MAP()
                      };

Open in new window


At the head of the file – SudokuStatusBar.cpp, before the IMPLEMENT_DYNAMIC add the following:

static UINT indicators[] =
                      {
                          ID_SEPARATOR, 
                          ID_SEPARATOR,
                          ID_SEPARATOR
                      };
                      #define PANE_HINT 1
                      #define PANE_INFO 2

Open in new window


This is the basic collection of 3 panes we want in the status bar.

Now add a WM_CREATE message handler and modify it to be this:

int CSudokuStatusBar::OnCreate(LPCREATESTRUCT lpCreateStruct)
                      {
                          if (CStatusBar::OnCreate(lpCreateStruct) == -1)
                              return -1;
                      
                          if(!SetIndicators(indicators, sizeof(indicators)/sizeof(UINT)))
                              return -1;
                      
                          SetPaneInfo(0, ID_SEPARATOR, SBPS_NOBORDERS | SBPS_STRETCH, 0);
                          SetPaneInfo(PANE_HINT, ID_SEPARATOR, SBPS_NORMAL, 100);
                          SetPaneInfo(PANE_INFO, ID_SEPARATOR, SBPS_NORMAL, 80);
                      
                          return 0;
                      }

Open in new window


We also need some more functionality to cope with the display.  Add the following to the header file for the custom statusbar.

public:
                          void SetInfo(const CString& szInfo);

Open in new window


And the following into the cpp file for the statusbar

void CSudokuStatusBar::SetInfo(const CString& szInfo)
                      {
                          SetPaneText(PANE_INFO, szInfo);
                      }

Open in new window


In the header file for the CMainFrame we need to add a #include “SudokuStatusBar” after the #pragma once and change the CStatusBar m_wndStatusBar into CSudokuStatusBar.

protected:  // control bar embedded members
                          CSudokuStatusBar  m_wndStatusBar;
                          CToolBar    m_wndToolBar;

Open in new window


In the OnCreate of the CMainFrame we need to make a change:

This:

    if (!m_wndStatusBar.Create(this) ||
                              !m_wndStatusBar.SetIndicators(indicators,
                                sizeof(indicators)/sizeof(UINT)))
                          {
                              TRACE0("Failed to create status bar\n");
                              return -1;      // fail to create
                          }

Open in new window


Becomes

    if (!m_wndStatusBar.Create(this))
                          {
                              TRACE0("Failed to create status bar\n");
                              return -1;      // fail to create
                          }

Open in new window


And the DisplayStatusInfo function needs to be:

void CMainFrame::DisplayStatusInfo(const CString& szInfo)
                      {
                          m_wndStatusBar.SetInfo(szInfo);
                      }

Open in new window


You can now compile and run the app, load the saved game and see the status bar.

Why did we pass a const CString& in function such as the following?

void CSudokuStatusBar::SetInfo(const CString& szInfo)

Open in new window


We need to pass the string information, passing it by reference (&) means that it is not constantly being copied into a new CString variable - efficiency.  

We have created our own statusbar to encapsulate the updating of the display elements.  Should we decide to add an extra pane to the status bar or to rearrange the layout in future then we don’t need to change a lot of code in other classes.  Note we even use a #define for the different pane indexes – to rearrange the layout is then just changing a #define value.

Conclusion:

Here we customised the default MFC status bar to provide visual feedback for the user.


Click here for the source code for this article


Previous article in the series is here:  Sudoku in MFC: Part 3
There we loaded and saved data to file on the hard disc.  We also investigated the interaction between the document and view based classes in the application architecture.

Next article in the series is here:  Sudoku in MFC: Part 5
Here we will be providing keyboard support (in PreTranslateMessage) to navigate the grid in response to arrow keys, we will also be entering numbers into the grid cells from the keyboard.


Two points to bear in mind.
You may use the code but you are not allowed to distribute the resulting application either for free or for a reward (monetary or otherwise).  At least not without my express permission.
I will perform some things to demonstrate a point – it is not to be taken as that meaning it is a ‘best’ practice, in fact an alternative might be simpler and suitable.  Some points in the code would even be called poor design and a possible source of errors.
1
4,138 Views
AndyAinscowFreelance programmer / Consultant
CERTIFIED EXPERT

Comments (0)

Have a question about something in this article? You can receive help directly from the article author. Sign up for a free trial to get started.