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.
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",
WS_CHILD|WS_VISIBLE,
CRect(10,10,350,200),
static_cast<CWnd*>(this),
GRAPH_CTRL_ID
);
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);
Sleep(50);
}
}
int CDemoDialog::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
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)
{
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;
case CUSTOM_GRAPH:
break;
}
}
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)
ON_COMMAND(GRAPH_CTRL_ID,OnClickGraph)
END_MESSAGE_MAP()
BOOL CDemoDialog::OnClickGraph()
{
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
.