Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

CStatusGraphCtrl - a C++ wrapper class on CWnd, to display status graphically

0.00/5 (No votes)
1 May 2004 1  
C++ source to create a control that graphically displays the current status of an application variable.

Introduction

The article explains how to integrate my Graphical Status display control (CStatusGraphCtrl) with your application. Many real-time applications will have a requirement to update the user with the current value of some of the critical parameters related to that application; CStatusGraphCtrl not only displays the values but also shows the progress of the change in the parameter as a graph. Currently, it supports Bar Graph and Line Graph displays. You may customize the code to support your own way of graphs, if required. The control is exactly similar to the one you see in the Windows Task Manager to display the CPU and memory usage.

Top - Bar Graph///Bottom - Line Graph

Using the code

CStatusGraphCtrl source (zip) will include the following files:

  • CGraphData.h
  • CGraphData.cpp
  • CStatusGraphCtrl.h
  • CStatusGraphCtrl.cpp

These four files are the core files for the control. Add these four files into your VC++ application project.

CGraphData class acts as a holder for the data that is currently shown in the control's viewport. The data is held inside as a linear singly linked list.

CStatusGraphCtrl class is the core control class that is derived from CWnd MFC Class. To create a CStatusGraphCtrl control into your dialog, call CStatusGraphCtrl::Create in your Dialog's CDialog::OnCreate as follows:

m_ctrlStatusGraph.Create("Demo Control",/* Caption - ofcourse not used */
            WS_CHILD|WS_VISIBLE, /* Windows styles */
            CRect(10,10,350,200), /* Position, size */
            static_cast<CWnd*>(this), /* Parent */ 
            GRAPH_CTRL_ID /* ID that u assign for this control */
            );

This creates a StatusGraphCtrl in the desired position. Once the control is created, you need to specify the values for a few properties of the control using:

  • SetMinValue
  • SetMaxValue
  • SetSamplingInterval

SetMinValue is called to specify the value of the bottom-most line in the control's viewport. SetMaxValue is called to specify the value of the top-most line in the control's viewport. SetSamplingInterval is called to specify the horizontal distance (in pixels) between successive plots. When everything is ready, call CStatusGraphCtrl::StartUpdate to put the control into action. Once StartUpdate is called, any call to CStatusGraphCtrl::SetCurrentValue will be reflected in the graph seen in the control's viewport.

The graph keeps scrolling from right to left. The delay between each scroll can be controlled by calling CStatusGraphCtrl::SetRefreshDelay with the required delay in milliseconds as argument.

The following block of code is taken from my DemoApp for this control and would give a simple code to make use of the CStatusGraphCtrl control in your application.

void DataGenerator(LPVOID param)
{
    CStatusGraphCtrl* pCtrl = (CStatusGraphCtrl*)param;
    while(true)
    {
        int nRand = rand();
        while(nRand > pCtrl->GetMaxValue())
            nRand /= 10;
        pCtrl->SetCurrentValue(nRand); 
        /* this value will be reflected in 
        the graph in control's viewport */

        Sleep(50);
    }
}

int CDemoDialog::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
    // m_ctrlStatusGraph is a member of type CStatusGraphCtrl


    m_ctrlStatusGraph.Create("Demo Control",
        WS_CHILD|WS_VISIBLE,CRect(10,10,350,200),
        static_cast<CWnd*>(this),GRAPH_CTRL_ID);
    m_ctrlStatusGraph.SetMaxValue(100);
    m_ctrlStatusGraph.SetMinValue(0);
    m_ctrlStatusGraph.StartUpdate();
    AfxBeginThread((AFX_THREADPROC)DataGenerator,
                      (LPVOID)&m_ctrlStatusGraph);
    return CDialog::OnCreate(lpCreateStruct);
}

Customizing CStatusGraphCtrl

Almost every aspect of CStatusGraphCtrl is customizable. The following are the list of properties which you can customize as per your requirements:

  • SamplingInterval - call SetSamplingInterval(short) - horizontal distance in pixels between successive plots
  • RefreshDelay - call SetRefreshDelay(int) - Delay in milliseconds between successive scrolls
  • Background color - access the public member COLORREF m_BackColor
  • Foreground color - access the public member COLORREF m_ForeColor

Adding custom GraphModes

To add your own GraphMode, all you need to do is just two steps. Update the enum StatusGraphType to add entry for your new GraphType in CStatusGraphCtrl.h header file. Then add the code to draw the graph, in DrawPoint function in CStatusGraphCtrl.cpp file. See this code snippet:

void DrawPoint(CStatusGraphCtrl* TheCtrl,long int cury,bool Update)
{
    // control specific code will be here...

    // ..

    // ..


    switch(TheCtrl->GetGraphMode())
    {
    case BAR_GRAPH:
        pdc->MoveTo(rcBounds.right-sSamplingInterval,rcBounds.bottom);
        pdc->LineTo(rcBounds.right-sSamplingInterval,rcBounds.bottom-cury);
        break;
    case LINE_GRAPH:
        pdc->MoveTo(rcBounds.right-2*sSamplingInterval,
                rcBounds.bottom-TheCtrl->m_lPreviousY);
        pdc->LineTo(rcBounds.right-sSamplingInterval,rcBounds.bottom-cury);
        break;
    //**************************************************************************//

    // TODO:

    // Update the enum type in the header (CStatusGraphCtrl.h) file accordingly

    //    Add additional case blocks to support your own graph modes

    //    cury - represents the current value to plot

    //    TheCtrl->m_lPreviousY - represents the previous point plotted.

    //    rcBounds - represent the bounds of the CStatusGraphCtrl Control.

    //*************************************************************************//


    case CUSTOM_GRAPH:
        // do something with cury,TheCtrl->m_lPreviousY,rcBounds as required.

        break;
    }

    // control specific code will be here...

    // ..

    // ..

}

Events

CStatusGraphCtrl is written in such a way that it sends a WM_COMMAND message when a mouse is clicked on the control. The application shall make use of this, as required. For example, in my DemoApp, I have added the functionality of changing the graph mode of CStatusGraphCtrl in the ON_COMMAND handler of the control. See the handler code:

BEGIN_MESSAGE_MAP(CDemoDialog, CDialog)
    // .. other message maps..

    // ..

    ON_COMMAND(GRAPH_CTRL_ID,OnClickGraph)
END_MESSAGE_MAP()

BOOL CDemoDialog::OnClickGraph()
{
    // toggles the graph mode between BAR_GRAPH (0) and LINE_GRAPH (1)

    m_ctrlStatusGraph.SetGraphMode((StatusGraphType)
              !(int)m_ctrlStatusGraph.GetGraphMode());
    return TRUE;
}

Points of Interest

I will tell you the way I brought up this code. I was coding a networking utility to monitor the network traffic and control excessive traffic from a single source, wherein I wanted a control like this. I started with the BS_OWNERDRAW feature of Button and happily coded with CDC for the requirement. This code uses CDC::BitBlt to perform the scrolling of the Graph and so is very fast and flicker-free. I was almost done, and checked the various functionalities for the control. Till then, I hadn't tried switching between applications when my application was running. And, the problem came in when switched across apps.... When I switched back to my application, the entire Graph was gone!!!!! Ooops, I didn't have necessary information left in my memory to repaint the Graph. That is when I added the new class CGraphData which stores data associated with the current viewport of the control. The data structure stores only the sequence of points and not the entire viewport pixel matrix. In fact, the scrolling happens in the data structure too. For every new value in the right, the value in the left will be deleted, thereby maintaining the window size.

Thanks to CodeProject members: I got feedback about my code, the first day I submitted this article; from which I could feel that use of CButton for this control is not at all necessary; I modified the code immediately and I am posting it here. Now the control is no longer using CButton. Its just a Window.

History

  • First version of CStatusGraphCtrl was derived from CButton.
  • Based on the feedback from CodeProject members, I modified the code to derive from CWnd.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here