Some of you may have already come to realize I have previously written and released a similar control via CodeProject. Even though the last control was written using C++ along with MFC, some readers may wonder why I would recreate the same control without branching out into uncharted territory. But the fact of the matter is: I am indeed branching out, and this isn't simply a recreation.
I've never ventured into C# before. So rather than jump the gun and start out with an ambitious project that is nothing but of a stranger to me, I have decided to stick with something I am both familiar and comfortable with: porting my C2DPushGraph control to C#. After porting the control and discovering C# wasn't much different than C++ in ideology, I quickly shifted my focus to improving the feature set and overall design of the control, all while conforming to general C# standards and ethics.
That being said (or rather typed), the basis of the control remains the same. So my previously written CodeProject article's introduction also applies here:
"In past programming experiences, I've often had a desire to display and monitor the rate or performance of some operation on a graph. Whether it be the rate of write operations or the number of content results extracted each second, I always thought a graph similar to the graphs found in Windows XP’s Task Manager (CTRL-Alt-Delete) performance and networking tabs would do the trick well. After a quick search on Google for the graph control clone yielded no relevant results, I decided cloning it myself would be an interesting and beneficial experience. As time progressed and cloning the control proved itself easy, I decided to add more features and make it completely customizable in nearly every aspect. In the end, as the smoke cleared, I was left face to face with the control this article revolves around. It looked and behaved astonishingly beautiful for being developed with such haste. But who cares about its pastime, let's move on and see how easy it is to implement..."
Using the Code
Adding the Control to your Windows Form
The easiest and simplest method of adding the
C2PushGraph control to a Windows Form is by first adding it to your control toolbox in Visual Studio. To accomplish this, you can follow these steps in Visual Studio 2005:
- Download the control source
- Extract the control's DLL to your project directory
- Open your project
- Open Visual Studio's “Tools” menu item
- Click “Choose Toolbox Items”
- Click the Browse button
- Locate the C2PushGraph DLL, and click “Open”
After performing these steps, you'll be able to drag and drop the
C2DPushGraph control into your form from the Toolbox (under Common Controls) just like any other standard control. To continue following along with the tutorial using this method, go ahead and drag the control to your form; then, name it
The alternate method of adding the control to your form involves manually coding the control instantiation and its creation within your form. While seldom used, this method proves itself useful if you want to add the control “on the fly”.
The first suggested step is including the
CustomUIControls.Graphing namespace in the scope of your cs file. This is of course done by including “
using CustomUIControls.Graphing” at the head of the file.
Next, you'll need to add a new
C2DPushGraph variable to your form class (for the sake of this tutorial, we'll call it
m_PushGraph). Now, in the method where you'd like to create the control, use one of the following code blocks as a guide to create and add the control to your form:
m_PushGraph = new C2DPushGraph( this,
new Rectangle( Xpos, Ypos, GraphWidth, GraphHeight ));
m_PushGraph = new C2DPushGraph( this );
m_PushGraph = new C2DPushGraph();
this.Controls.Add( m_PushGraph );
Setting the Graph Range
After instantiating the
C2DPushGraph variable, you'll need to set your graph's range. A range consists of the highest and lowest magnitudes that you intend to push to a line. An example would be setting the range of a CPU usage graph from 0 to 100.
You set the range by setting '
MinPeekMagnitude' to the smallest numerical magnitude you may potentially push to a line within the graph. Inversely, '
MaxPeekMagnitude' specifies the maximum potential magnitude.
AutoAdjustPeek is set to
true (by default it isn't), it is suggested that you only provide a 'rough range'. When this mode is enabled, the range is automatically adjusted to contain any magnitude.
Adding a New Line
Now that we have our basic graph all ready to roll, we'll want to add one or more new lines to it. But before we dive any deeper into the conventions of calling this method, it's important to understand how lines are identified.
A line can either be uniquely identified using a numerical ID (slightly better performance) or by using a name (more convenient). This identification provides us with a way to obtain references to lines at anytime without having to keep track of the line handles. It is important to note that the choice of using IDs or just obtaining and storing the line handle after line creation is a matter of personal preference (you could even use a combination of both).
To add a new line, we call the control's conveniently named '
AddLine' method, which in turn creates and adds a line to our graph, and then returns the line handle. '
AddLine' requires two parameters: the desired numerical ID or name for future reference, and the line's initial color. If the line is successfully added to the graph, the line's '
C2DPushGraph.LineHandle' reference will be returned. If the passed numerical ID or name is not unique, a null value will be returned:
const int EXAMPLE_LINE = 47;
MyLineHandle = m_PushGraph.AddLine( EXAMPLE_LINE, Colors.Blue );
if (MyLineHandle == null)
MyLineHandle = m_PushGraph.AddLine( "My Example Line", Colors.Blue );
if (MyLineHandle == null)
Pushing a New Magnitude Point to your Line
Pushing a new point to our newly created line is anything but non-trivial. The first thing to do, is to call the control's '
Push' method, and pass the line's ID or name, in addition to the magnitude you'd like to plot. If your graph contains more than one line, you should perform the same process for all the other lines displayed on the graph before continuing. An example of this process follows below:
const int EXAMPLE_LINE = 47;
m_PushGraph.Push( EXAMPLE_LINE, 20 );
m_PushGraph.Push( "My Example Line", 76 );
Updating the Graph
With our graph fundamentally established and our first set of magnitudes pleasantly awaiting their visual debut, our last required step is to update and redraw our
C2DPushGraph control. If you look back to the previous code example, you'll likely (and hopefully) notice it introduced a new unexplained method: “
UpdateGraph's written explanation can be short due to its simplistic nature. It does exactly as the name implies; it updates our graph control to take any newly pushed magnitudes or lines into account when rendering the control (which it also does). Optimally, you should only call “
UpdateGraph” after pushing an equal number of magnitudes to each line included in your graph; but such methodology is not required (see Additional Notes).
Customizing the Appearance and Behavior of your Graph
Although our graph is now incorporated and completely functional within our application (assuming you've been referring to this tutorial as a general guide to implementing the control), we still have a great deal of features to explore. All of these features involve properties to adjust aesthetics and behavior. While altering them is purely optional, they'll provide greater depth and visual splendor to your application.
To provide you with a mental representation of the general graph components, refer to the following image:
Gets or sets a boolean value indicating whether the graph automatically adjusts its range to include any magnitude pushed to its lines (auto scaling).
Gets or sets the background color of the graph.
Gets or sets the background image to be used for the graph (overrides the background color).
Gets or sets the graph's grid color.
Gets or sets the width/height (in pixels) of each square in the graph's grid.
Gets or sets a boolean value indicating whether the graph is rendered in 'high quality' mode (with antialiasing). It is suggested that this property be set to
false if you intend to display your lines using bar graph styles, thickness greater than two, or if maximum performance is absolutely crucial.
Gets or sets the number of pixels between each displayed magnitude.
Gets or sets the string to display as the graph's 'maximum label'.
Gets or sets the string to display as the graph's 'minimum label'.
Gets or sets a boolean value indicating whether the graph's grid is to be displayed.
Gets or sets a boolean value indicating whether the minimum and maximum labels are to be displayed.
Gets or sets the color of the labels displayed in the graph (labels).
Customizing the appearance of your lines
The appearance of individual lines can be altered by changing properties using the line's handle. If you have not retained handles for each of your lines, they can be retrieved by calling “
GetLineHandle” and passing each subsequent line's numerical ID or name as the only parameter. Once you have the line handle, you may access the handle's following public properties:
Sets or gets the line's current color.
Gets or sets a boolean value indicating whether this line's magnitudes are displayed in a bar graph style.
Sets or gets the line's thickness in pixels. Note: It is advisable to set your control's
HighQuality property to
false if using a thickness greater than two pixels, as the antialiasing creates imperfections.
Gets or sets a boolean value indicating whether the line is visible.
- This graph control does not automatically update itself at regular intervals; it is up to the developer to produce such functionality through timers or other techniques.
- To remove a line, call the control's '
RemoveLine' method with either the line's numerical ID or name as the only parameter. If the line is successfully removed, the return value will be '
- To check if a line exists, call the control's '
LineExists' method with either the line's numerical ID or name as the only parameter. If the line exists, the return value will be '
- You can clear a line's magnitudes by calling the line handle's '
- It is suggested that you disable 'High Quality' if using one or more line(s) with a thickness greater than two pixels due to imperfections caused by antialiasing.
- Although frowned upon, it is possible to push magnitudes to lines unequally (i.e.: two to one line, one to another line) prior to updating the graph. The graph automatically compensates and displays the lines without interruption.
- The only reason this class is named
C2DPushGraph (using a Hungarian derivative) instead of
2DPushGraph, is due to the inability to name classes using a leading numeral.
Contacting the Author
If you have any questions or comments regarding this control, please feel free to send Stuart Konen an e-mail at firstname.lastname@example.org.
Stuart Konen can also be contacted via AOL's Instant Messenger: Screen name 'Griblik3'.
- November 28th, 2006 - Began work on
- December 2nd, 2006 - Submitted
C2DPushGraph to The Code Project.
- January 14th, 2008 - Added fix for potential hang with locked workstations.
Well first of all, it is fairly obvious my name is Stuart Konen (I'm sure 50% of you just took notice), all of my life I've lived on a farm located in Northern Idaho. What shatters the stereotype of rural residence however, is the fact that I'm very active in the technology and programming worlds. I took up the hobby of programming at age 9, at that point it was little language known as Quick Basic *sigh*. Fast forward another 9 years... (Woah... I just realized that's half of my existence. But that's something I'll have to contemplate later, as I have an autobiography to tell).
Now my experience in programming has improved vastly, I've released various technologies and programs and I'm continuing to pump out concepts and systems that are getting glances from all over the world. My weapon of choice is C++, the core language of the majority of freshly released software, it's sleak, mean, and incrediably powerful. On the side I venture into web application development and site design, where my interest lies in PHP. Over the years my project have included everything from Artificial Intelligence to Web Statistic Tracking systems, but that's the past. What matters is the future... Remember that question we were always asked in grade school? Where did we see ourselves in 10 years. Well that question was asked about 8 years ago in my life, so it looks as though I only have two more for planning stages. In two years I see myself plunging into the world of research, creating my own Research and Development firm, aiming to meet the never-ending need for new and superior software and technology. Soon after becoming a multi-billionare I'll pursue my dream of world domination. Nobody is perfect...
Actually when it comes down to things, the money has no meaning. But there you have it, a 5 minute slice of my thoughts and time... If you have any job opportunities or have the slight urge to initiate a conversation with me, it can be done via email: skonen [at] gmail [dot] com