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

CHistogramCtrl, A Windows 2000 Like Histogram Control

0.00/5 (No votes)
12 Apr 2001 1  
A histogram control that is used to plot specified values within the defined range (limits)

Initial Idea

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 dialog class.

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:

C++
// Declaring a local variable
CRect rect;
// Get the rectangular coordinates of the rectangle 
// we already placed on dialog
GetDlgItem(IDC_STATIC_HISTOGRAM)->GetWindowRect(rect);
// Convert to the client coordinates
ScreenToClient(rect);
// Create and place the histogram control on screen
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:

C++
BOOL CHistogramCtrl::SetGridsColor(COLORREF cr);

Where cr is the grids' color reference. For example, you can change the color of grids to red this way:

C++
//Change the color of grids to red
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:

C++
BOOL CHistogramCtrl::SetBkColor(COLORREFcr);
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:

  1. LOW_SPEED (3000 ms)
  2. NORMAL_SPEED (1000 ms; default value)
  3. HIGH_SPEED (500 ms)
  4. IDLE (0 ms)

To use this function, all you've to do is as follows:

C++
// Change the speed of scrolling
m_ctrlHistogram.SetSpeed(CHistogramCtrl::HIGH_SPEED);</td>

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:

C++
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:

C++
//Set the current position of the histogram to 26
m_ctrlHistogram.SetPos(26);

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:

C++
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:

C++
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!

History

  • 13th April, 2001: Initial version

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