The initial idea for doing this code was put into my mind when my love asked me to go to a clinic in order to check out my heart. You know, doctors have got some sort of plotters that plot the information obtained from the heart on a piece of paper as a histogram. The idea came into my mind from there! This control is sincerely dedicated to my love, Cindy.
How to Use
To use the control, you import the header file as well as the .cpp file attached to this article. Then you simply open the dialog editor and place a rectangle on your dialog. We call this static control
IDC_STATIC_HISTOGRAM. Thereafter, you instantiate an object of the
CHistogramCtrl class, say
m_ctrlHistogram, as a data member of your
In the message handler of the
WM_INITDIALOG message, you get the area of the static rectangle you already placed on your
dialog box and you simply create the Histogram Control as follows:
m_ctrlHistogram.Create(WS_VISIBLE | WS_CHILD
| WS_TABSTOP, rect, this, IDC_STATIC_HISTOGRAM);
After doing this, the histogram's window is shown on screen in the default colors assigned to the control within the constructor of the class. For example, the background color of the control is held in the
m_crBackGround member variable of the class and is assigned to the color black in the constructor of the
CHistogramCtrl. The other member variable,
m_crGrids holds the grid color and is assigned to
RGB(0, 130, 66) in the constructor of the class.
What if we would like to change the grid color to red? Shall we alter the mentioned variables within the constructor? Of course, not! To do so, I've created a method,
SetGridsColor, prototyped as follows:
BOOL CHistogramCtrl::SetGridsColor(COLORREF cr);
cr is the grids' color reference. For example, you can change the color of grids to red this way:
m_ctrlHistogram.SetGridsColor(RGB(255, 0, 0));
Here are the other methods of the class that can be used to change the default behavior of the control:
void CHistogramCtrl::SetPen(int nWidth, COLORREF crColor);
void CHistogramCtrl::SetRange(UINT uLower, UINT uUpper);
CHistogramCtrl::SPEED SetSpeed(CHistogramCtrl::SPEED uSpeed);
void CHistogramCtrl::SetPos(UINT uPos);</td>
Although the method names clarifies their purposes, it makes sense if we have a quick look at what each one is useful for.
The first above-mentioned method,
SetBkColor, is used to set the background color of the control. The default value is black, i.e.,
RGB(0, 0, 0), declared in the constructor of the class.
The second method is used to set the control's pen, the one that's used to draw the histogram. The first parameter,
nWidth, is used to set the width of the line. The second parameter,
crColor, is a
COLORREF that sets the color of the pen. If you don't use this function, the default values are used,
1 as the pen width, and
RGB(0, 255, 0) as the pen's color.
The third method,
SetRange, is used to set the upper and lower limits (range) of the control. The valid range for these arguments is as follows:
uLower > 0
uUpper > 0
uLower < uUpper
Please note that the range will be set to 1-100 if you don't specify a range - i.e., you can plot numbers between 1 to 100.
Now let's move from
SetRange to the next method,
SetSpeed. This is used to control the refresh speed of
CHistogramCtrl. Let me say that the window contents are shifted 3 pixels to the left within the specified intervals. There are 4 predefined intervals declared as an
enum as follows:
LOW_SPEED (3000 ms)
NORMAL_SPEED (1000 ms; default value)
HIGH_SPEED (500 ms)
IDLE (0 ms)
To use this function, all you've to do is as follows:
This way, the contents of the control is shifted 3 pixels to the left at 500ms intervals.
Now it is time to turn our attention to the last
public method of the class,
SetPos, declared as follows:
void CHistogramCtrl::SetPos(UINT uPos);
This member function does the actual plotting. For example, to set the current position of the histogram to
26, you can easily write the following statement:
Now imagine what happens if you set the position of the histogram to 87 ten seconds later. You'll see a histogram as follows:
In other words, there will be no change in the line if we don't feed the control with new values. But what happens if we feed the control more than it can show? For example, imagine that we've set the speed of the control to
HIGH_SPEED (500ms) and we feed the control 20 times within the 500ms. Which value is shown at the end of the first 500ms?
To solve this problem, I created a
CList as a
private data member of
CHistogramCtrl. Whenever you call
SetPos, I append the value you provide to the mentioned list. Whenever I want to refresh the control (at the end of each 500ms in our example), I process the list and obtain the average value of the current values within the list. To clarify what I'm talking about, imagine that we set the current position of the control to the following values within the first 500ms:
10, 87, 19, 54, 63, 74
When this time elapses, I process the current values within the list and calculate the average of the list members:
10 + 87 + 19 + 54 +63 + 74 = 307
307 / 6 = 51.1
And I show
51.1 as the current value... Got it?
This is done via one of the
private methods of the class called,
GetAverage, that returns the average of numbers within the mentioned list. In some cases, you have to change the mechanism of calculating the current position of the control within the refresh intervals with your desired one. For example, I've used the variance of the numbers instead of their average in my latest project. Please note that the function is called within the
DrawLine method to obtain a value to show at the end of each period.
Since the source code is straight forward and easy to understand this article is finished now. Please feel free to send your comments, questions and/or suggestions about this control by leaving a comment below. Aloha!
- 13th April, 2001: Initial version