Click here to Skip to main content
Email Password   helpLost your password?
HBarChartControl_src

Contents

Introduction

HBarChart is a simple C# control. It helps you develop your own charts quickly and print them easily.

Background

A while ago, I created an MFC bar chart control and shared it here on The Code Project. In a discussion below that article, I promised to create a C# version of the same control, and here it is. The only problem is that probably the code looks more like C++ than C#. I hope you'll forgive me; this is one of the few C# codes I've ever written.

Enjoy, and please help me with invaluable notes, bugs reports, ideas, etc. that you think might will improve the quality of this code.

Using the Code

Creating the Chart

To use this control, first we have to add it to a project. You can use one of the following two methods to add it.

Add a Reference to the Project

  1. Copy the BarChart folder, available inside the CPBarChart source folder attached to this article, and all its contents into your solution folder.
  2. Right click on the Solution Explorer of Visual Studio, and select: Add Existing Project, and select the BarChart folder.
  3. Select the project you want to add the chart to and right click on it in the Solution Explorer.
  4. Select Add Reference, select the Project tab of the dialog that opens, and select BarChart, then recompile the solution.
  5. In the Visual Studio toolbox window, you should be able to see a new group named [YouProjectName] Components, inside which the HBarChart item is visible.
  6. Drag the HBarChart component onto any Windows Forms control.

Add the DLL

  1. In the Visual Studio toolbox, right click at the top of a category (like General) and select Choose Items.
  2. In the .NET Framework Components tab, find the Browse button, and using the Browse window, select BarChart.dll.
  3. Now, drag the new bar chart control that's added to the toolbox, on to a form.

You can alternatively drag the DLL to the Visual Studio toolbox.

Chart Background

The background of the chart could be either linear/radial gradient or solid. Gradient uses two colors to draw background of the chart.

To set the background mode, use Background.PaintingMode. It's of type enum, and could be set to either SolidColor, LinearGradient or RadialGradient.

The chart background also has to define the color. The color depends on the current mode selected. If a gradient is selected, there are two colors to be set: GradientColor1 and GradientColor2. If a solid color is your choice of the background, then SolidColor should be identified. The background default mode is radial gradient, and the default colors are: Argb(255, 140, 210, 245) and Argb(255, 0, 30, 90) and could be modified in the CBackgroundProperty() constructor. To change mode or color in your code, use Background property of the chart:

barChart.Background.PaintingMode = CBackgroundProperty.PaintingMode.LinearGradient;
barChart.Background.GradientColor1 = Color.Aqua;

Chart Description

The description is the bottom-most descriptive line of text. In the snapshot, it is: "Hello C# Chart." Its usage is to describe what the chart shows. You can modify its Text, Color, Font, and Visibility.

barChart.Description.Font = fontDialog.Font;
barChart.Description.Text = "Annual coffee usage of c# team.";
barChart.Description.Color = Color.White;
barChart.Description.Visible = true;

Labels and Values

To make the chart more flexible, I made it possible to modify the labels at the bottom of each bar and also the value text at the top of each bar. Please note that while it's possible to modify the labels and the values, they can only be modified as a whole, and it's not possible to do it for each bar separately.

barChart.Label.Font = fontDialog.Font;
barChart.Label.Color = Color.White;
barChart.Label.Visible = true;

barChart.Values.Font = fontDialog.Font;
barChart.Values.Color = Color.White;
barChart.Values.Visible = true;

Instead of the value of each bar, it's also possible to use a percentage. In my MFC version of this chart which was created for a particular purpose, I needed to show the percentage of each value compared to the maximum value. It was considering the maximum value to have 100%. In this chart that is created solely for CodeProject members, I take a more useful, and probably more popular, approach, and the percentage of the value compared to the total values is used, i.e., only a lonely bar will have 100%, and by adding a second bar, each percentage shows the share of the total value the bar takes. Two bars with same values, in this case, each will have 50% of the total value.

barChart.Values.Mode = CValueProperty.ValueMode.Percent;

Border

HBarchart can display a solid border around the chart. Its size and color can be customized.

barChart.Border.Color = colorDialog.Color;
barChart.Border.Width = 5;

Shadows

Two types of shadows can be displayed around the chart; Inner and Outer. Inner shadow starts from inner rims of the bounding rectangle of the chart and moves towards center of the chart, and outer shadow starts from outer sides of the bounding rectangle and move outside. You can set the chart to draw any of them, both of them or none of them.

// Just Inner shadow, could be: Outer, Both, None
barChart.Shadow.Mode = CShadowProperty.Modes.Inner;

You can also set size of the shadow:

barChart.Shadow.WidthInner = 2;

And finally color of the shadow:

barChart.Shadow.ColorInner = Color.Black;

Please note that transparency (alpha) of the color of the shadow is important to have a stronger (darker) shadow or a lighter one.

Sizing

The last customization of the chart is about sizing. The chart has two sizing modes. The Normal mode has no limitation to any drawing. For example, you can change fonts so that parts of texts are out of the chart control, that makes them invisible. The Normal sizing mode along with fonts, colors, texts, BarSize and BarGapSize (white space between bars) makes it possible to create a fully customized chart.

The other possible mode is AutoScale, in which case, the control decides the sizes. It simply tries to fit the chart in its bounding rectangle. Your desired colors and fonts will be used, but the size of the fonts and bars are calculated automatically.

barChart.SizingMode = HBarChart.BarSizingMode.AutoScale;

After customizing the chart to your will, it's time to use it, by adding new values, retrieving, or removing them.

Adding Values (Bars)

To add a new value to the chart, use the Add method as follows:

barChart.Add(
   1600.356,           // A value to be shown at the top of the bar
   "Jan",              // A label under the bar chart
   Color.Aqua);        // Color of the bar

Note that the control uses this color (the last parameter of Add) to create a gradient for the bar, which in my humble opinion is more polished. The gradient's darker color is 100 units darker, so to get best results, please use colors with their (R, G, B) all over 100. E.g., Color.FromArgb(255, 150, 160, 255).

You can alternatively use Items property directly. It's an IList collection.

barChart.Items.Add( new HBarItem(
   1600.356,           // A value to be shown at the top of the bar
   "Jan",              // A label under the bar chart
   Color.Aqua));        // Color of the bar

Removing Values

You can use RemoveAt to remove bars from the control for a specific zero based index.

barChart.RemoveAt(0);  // Removing first value

Or again use Items collection.

barChart.Items.RemoveAt(0);  // Removing first value

Modifying Values

Call ModifyAt to change value of a bar.

barChart.ModifyAt(0, 6250);  // Changing value of first bar

or change values directly.

barChart.Items[0].Value = 6250.1148;  // Changing value of first bar

Inserting a Bar

Call InsertAt or Items.Insert to insert a bar at a specified zero based index.

barChart.InsertAt(
                3,           // Where bar will be inserted
                             // (zero based index of this new bar)
                2650.8265,   // Value of the new bar
                NewLabel,    // Label of the new bar
                Color.Red);  // Color of the new bar

Retrieving Values

To get a value or any other information about each bar, you can call the GetAt method. The method has two definitions: one that returns a double value corresponding to a bar when its 0 based index is given; the other overloaded function returns an HBarItem object. HBarItem is a class that holds all data about a bar, including its value, color, label, the bounding rectangle inside the chart and many more.

HBarItem bar;
if (barChart.GetAt(0, out bar))
{
  // Now we have all bar data
  double dValue = bar.Value;
}

Events

Other than normal set of events for a user control, there are four other events supported by HBarChart: BarClicked, BarDoubleClicked, BarMouseEnter and BarMouseLeave. To handle these events, you can follow one of these two methods:

In Visual Designer

Right click on the chart control on your form and select Properties. At the top of the properties window select Events button. Then head to Bar Chart section, you should see them. Double click on the title of any event you want to handle.

Events.jpg

Handle Events Manually

It's a two step job; first register for the event:

barChart.BarClicked += new BarChart.HBarChart.OnBarEvent(this.On_BarChart_BarClicked);

Second, add an event handler function:

private void On_BarChart_BarClicked(object sender, BarChart.BarEventArgs e)
{
    MessageBox.Show(String.Format("bar #{0} Clicked!", e.BarIndex);
}

Printing

Use the Print method to print the chart.

barChart.Print(true, "PrintDocumentName");

Print receives two arguments. The first one indicates whether the chart should be fitted to paper or should be printed with the same scale of the display. It acts similar to the sizing mode of the chart. The second parameter is the name of the print document. In my to do list, printing is marked as incomplete. There's a class in the BarChart namespace named CPrinter that, together with the Print method of the HBarChart class, is responsible for printing. If you need better printing support, you can modify them.

DataSource

Chart now has a minimal supports for Datasource. It can be connected to a data source. It reads data and responds to changes, but at the moment it's a one way connection and modified chart data will not be mirrored to the datasource. You can look at it as a read only DataSource support.

How chart uses DataSource

At the moment, when you set chart datasource, it expects you to provide a combination of rows and columns that each row displays a chart. For each column, there will be a bar that takes its value from the row. Demo application of this article has a DataGridView control on the form that shows how exactly chart works with data.

Extending Chart DataSource Capabilities

I designed the DataSource in a way that makes it possible to create your own way of reading data and displaying them. HBarChart has a data connection class that connects to datasource and retrieves rows and columns of data automatically. To display these rows and columns in chart, it uses a class that implements IDataConnectionEvents. If you want to be able to interpret rows and columns of data your way, just create a class that implements the interface:

public class CreateChartMyWay : IDataConnectionEvents

Then tell the chart to use your class:

this.barChart.DataSourceManager.DataEventHandler = new CreateChartMyWay();

Doing this, chart will work with data and calls functions that you implemented in your class when needed. It calls functions that are defined in the interface. By implementing IDataConnectionEvents your class will have these functions:

void DataSource_ItemUpdated(int nRowIndex, int nColIndex);
void DataSource_ItemDeleted(int nItemIndex);
void DataSource_ItemAdded(int nItemIndex);
void DataSource_SelectedRowChanged(int nPosition);
void DataSource_ResetItems();
void DataSource_DataBoundCompleted();

void SetData(object chart, object dataConnection);

Functions starting by DataSource_ will be called when a change occurs in the underlying data.

DataSource_ItemUpdated Will be sent after connection class receives ListChangedType.ItemChanged and indicates a change in a row.
DataSource_ItemDeleted Will be sent after connection class receives ListChangedType.ItemDeleted and indicates a row deleted.
DataSource_ItemAdded Will be sent after connection class receives ListChangedType.ItemAdded indicating a new row added.
DataSource_ResetItems Will be sent after connection class receives any of the following ListChangedTypes: Reset(Many roes changed), ItemMoved(some row indexes changed), PropertyDescriptorAdded(change in schema probably a new column), PropertyDescriptorDeleted(change in schema probably a column removed), PropertyDescriptorChanged(change in schema, a column changed).
DataSource_SelectedRowChanged Will be sent after connection class receives PositionChanged event. It means a new row is Current row of the DataSource.
DataSource_DataBoundCompleted It is called when a connection established to DataSource and rows and columns are populated with DataSource data.

Please note that when DataSource_ItemDeleted is called, data is not actually deleted from rows and columns of connection class, so that you can find data that is to be deleted easily. After the call however, it will be removed.

A final important note: the SetData function is called after your class is instantiated and will give you a refrence to both chart (of type HBarChart) and the connection (of type CDataConnection) classes. Chart reference can be used to interact with bars or other chart GUI related stuff and connection to work with rows and columns.

History

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
QuestionMouseEvents
geali_dor
20:56 14 Jan '10  
Hi! Good Job, but one question:

Why are there MouseEvents raised when i move the mouse above the bar f.e. on the value-label or even upper. i think this not correct?

hack the planet

QuestionRe: MouseEvents
Hamed Mosavi
4:03 15 Jan '10  
Hi!

geali_dor wrote:
Good Job

Thanks.


geali_dor wrote:
Why are there MouseEvents raised when i move the mouse above the bar f.e. on the value-label or even upper. i think this not correct?


I think it's a matter of taste. I thought my clients would like to have information with less effort. If you believe users don't like it in your app, then do this:

In HBarChart.cs at around line 1131 in a function called HitTest find this line:

if (bars[i].BoundRect.Contains(MousePoint))


replace it with this one:

if (bars[i].BarRect.Contains(MousePoint))



"I hope you live a life you're proud of. If you find that you're not, I hope you have the strength to start all over again."    
 - I wish I knew who is this quote from
 

Generalquestion about scaling the windows form smaller
derek0526
19:54 5 Jan '10  
I set SizeingMode in Normal.
How does it show left barcharts instead of middle ones when I scale the windows form smaller?
thanks
GeneralRe: question about scaling the windows form smaller
Hamed Mosavi
7:01 7 Jan '10  
Hi.
Alignment is not supported in this version. To do so you'll need to change source code. Aligning to the left is very easy:

In BarChart project find HBarChart.cs then go to line number 831:

nStartX = this.bounds.X + (this.bounds.Width - bars.Count * nBarWidth - (bars.Count + 1) * nBarsGap) / 2;


Change it to:

nStartX = this.bounds.X;



"I hope you live a life you're proud of. If you find that you're not, I hope you have the strength to start all over again."    
 - I wish I knew who is this quote from
 

GeneralAwesome
Xmen W.K.
3:03 14 Nov '09  
Looking pretty awesome, have a 5 Wink Thumbs Up



TVMU^P[[IGIOQHG^JSH`A#@`RFJ\c^JPL>;"[,*/|+&WLEZGc`AFXc!L
%^]*IRXD#@GKCQ`R\^SF_WcHbORY87֦ʻ6ϣN8ȤBcRAV\Z^&SU~%CSWQ@#2
W_AD`EPABIKRDFVS)EVLQK)JKQUFK[M`UKs*$GwU#QDXBER@CBN%
R0~53%eYrd8mt^7Z6]iTF+(EWfJ9zaK-i’TV.C\y<pŠjxsg-b$f4ia>

-----------------------------------------------
128 bit encrypted signature, crack if you can

GeneralRe: Awesome
Hamed Mosavi
8:42 14 Nov '09  
Thank you. Smile


"I hope you live a life you're proud of. If you find that you're not, I hope you have the strength to start all over again."    
 - I wish I knew who is this quote from
 

GeneralThere's a bug while using on tab control
avogt66
6:05 8 Nov '09  
Hi,
When I use it on tab control e.g. at page 3 and I set barChart.Shadow.WidthOuter greater than 0 then I see in the Shodow parts from the tab page I selected before.

But realy a good joob.
Thanks
GeneralRe: There's a bug while using on tab control
Hamed Mosavi
8:43 14 Nov '09  
Thanks.

That's right. I had plans to improve many aspects of this control. Sadly I can't find any free time these days or when I find some, I'm too tired to work on it. I'll finally do it.


"I hope you live a life you're proud of. If you find that you're not, I hope you have the strength to start all over again."    
 - I wish I knew who is this quote from
 

Generalgreat [modified]
İbrahim Ates
3:25 3 Nov '09  
Hey so many thanks,, this tool is so usefull,

modified on Tuesday, November 3, 2009 9:31 AM

GeneralRe: great
Hamed Mosavi
6:55 3 Nov '09  
Thank you. Smile


"I hope you live a life you're proud of. If you find that you're not, I hope you have the strength to start all over again."    
 - I wish I knew who is this quote from
 

GeneralHi...
S-Homayoon
19:56 27 Jun '09  
Hi Hamed...
Its Exellent..! Wink
GeneralRe: Hi...
Hamed Mosavi
20:25 29 Jun '09  
Glad you like it. Smile

Thank you.


"I hope you live a life you're proud of. If you find that you're not, I hope you have the strength to start all over again."    
 - I wish I knew who is this quote from
 

GeneralNice one
Mav202
10:40 7 Mar '09  
Hello
First, you have a super Jop made. Bravo.

A question
I want to fix the vertical scale, since my levels never be larger than 1024 and I like the reference to the value 1024 would see. So no car Scala from the axis 1024, but fixed.


HBarChart1.Items.ABSMaximum unfortunately that is "read only"

Can you help me.
GeneralRe: Nice one
Hamed Mosavi
19:14 8 Mar '09  
Hi.

HBarChart1.Items.ABSMaximum and Maximum are read only because they are calculated based on the numbers given tothe chart. Why do you need to change them?


Mav202 wrote:
my levels never be larger than 1024


What problem does that make? I mean you can set any value to each bar (1024, more or less). I'm sorry but I can't understand what you need from your message.

I only can guess you need to put an upper limit for the values that are added to the chart. A validation method or something like that. If this is what you are looking for, unfortunately you have to do it manually. You need to find functions that accept new values and add a validation function that verifies entered values. But in my opinion a chart shall not put any limit (other than underlying OS limits) to entered values. This control is used for presentation not data entry. Values passed to this controls shall be already validated in other layers like business logic. Adding validation responsibility to a chart implies a bad design in my opinion.

"In the end it's a little boy expressing himself."    Yanni


GeneralRe: Nice one
Mav202
2:39 9 Mar '09  
Hello
Thanks for the reply.
It is true it does not make sense for an Excel chart .
But i develop a CAN Bus Analyzer which can only receive values from 0-255 (respectively 0-1024).
For the display, for example, the visualitation of the limitation of the full throttle is usefull.
Thus we can see how much is the value of gas without watching closely.
I can send you the program, if you want to have a look at it.
Would be very grateful if you can provide a solution.

cheers from Switzerland
GeneralRe: Nice one [modified]
Mav202
2:47 9 Mar '09  
i think, if I can specify HBarChart1.Items.ABSMaximum it is what I need.

modified on Monday, March 9, 2009 8:01 AM

GeneralRe: Nice one
Hamed Mosavi
5:30 9 Mar '09  
Mav202 wrote:
i think, if I can specify HBarChart1.Items.ABSMaximum it is what I need.


I guess not.

Please look at how chart works more accurately. If all you want is an upper limit, then this is already implemented! Not forcing a maximum limit but if all given values are 1024 and less, the bar that fills 100% of charts' height will be the one with the value of 1024. Test it yourself. Set all values of this chart below 1024. Also set Values property to Digit(Not percent), now see how height of all bars change respectively.

If this is not what you need and if you can, prepare an image of what you want and I will be able to help more.

Thanks.

"In the end it's a little boy expressing himself."    Yanni


GeneralRe: Nice one
Mav202
11:06 9 Mar '09  
I have send you an E-Mail with Pictures to the following address: info@....
I will send you a picture of my programme tomorrow.

When i set all values to 1024, the Bar fills 100% of charts.
When i set all values to 512, the Bar fills 100% of charts.

I would like to set all values to 512 and then get the bar fills 50% of charts (from 1024).

Sorry, but my english ist not very well. I hope you will understand me.
Thanx a lot for you help.

cheers
GeneralRe: Nice one
Hamed Mosavi
20:25 9 Mar '09  
Mav202 wrote:
I have send you an E-Mail with Pictures to the following address: info@....


Big Grin That email is probably in the hands of CodeProject administration team wondering what is it!

I thought you mean info at CodeProject. I received your letter. Thanks.

Mav202 wrote:
When i set all values to 1024, the Bar fills 100% of charts.
When i set all values to 512, the Bar fills 100% of charts.


Very clear! I got it. I'm wondering what would you expect to see if value of a bar is bigger than 1024?

Find a function called ReCalculateAll() in HBarChart.cs file. It's a member of HBarItems class. It must be at around line 1717. I've modified my copy so line number is not accurate. Now find this phrase ShouldReCalculate = false; and a line before this add these lines of code:

 if (dABSMaximumValue < 1024)
{
dABSMaximumValue = 1024;
}


You can also instead of 1024 use a property or member variable that can be modified later from the outside world.

"In the end it's a little boy expressing himself."    Yanni


GeneralRe: Nice one
Mav202
6:35 10 Mar '09  
I think that's what I was looking for.
I will test it within the next days.
I will send you a picture of my program.

Thanks a lot for the super help.
Thanks Thanks Thanks Thanks Thanks
GeneralRe: Nice one
Hamed Mosavi
8:18 10 Mar '09  
Mav202 wrote:
I think that's what I was looking for.


I hope so.


Mav202 wrote:
Thanks a lot for the super help.
Thanks Thanks Thanks Thanks Thanks


You are welcome. Thank you for your interest. Smile

"In the end it's a little boy expressing himself."    Yanni


GeneralRe: Nice one
Mav202
3:39 11 Mar '09  
excellent.
it works. you have an e-mail.
Thank you very much.

best regards
GeneralRe: Nice one
Hamed Mosavi
3:57 11 Mar '09  
Mav202 wrote:
it works.


Glad to hear that. Smile


Mav202 wrote:
you have an e-mail.


Are you sure? I haven't received any new mail yet. Maybe my web server blocked it. It blocks emails that contain executable I guess. Send me a private email containing your email address using mail button below this message so that we can communicate via email if you like.

Thanks.

"In the end it's a little boy expressing himself."    Yanni


GeneralNice, but....
raymond.chen2008
23:05 18 Feb '09  
Nice and great job! But how to draw bars horizontally? Thanks in advance!
GeneralRe: Nice, but....
Hamed Mosavi
23:16 18 Feb '09  
raymond.chen2008 wrote:
Nice and great job!


Thank you.


raymond.chen2008 wrote:
how to draw bars horizontally?


Not supported yet. Almost a long while ago I started a new project aiming to rewrite this control, incorporating some design patterns that I recently learned. At the middle of this job, I had to deliver some of my works sooner than planned so I'm too busy to finish that up right now.

If it takes even longer, I'll upload this new (incomplete) project to my website and put a link to it here, so that anyone interested can continue working on it.

"In the end it's a little boy expressing himself."    Yanni



Last Updated 10 Jul 2008 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010