C# Draw a horizontal line on each stacked column chart - c#

Currently I need to develop a tool with chart as the major component. Chart control is also new for me. I have do a lot of reading, researching to learn and understand the whole picture of the chart control.
After all, I had stuck and question on how to draw a horizontal line (blue & red horizontal line) on stacked column chart as image shown below:
Here what I have done so far:
This is my code so far:
// X-Axis labels settings
chart.ChartAreas[0].AxisX.LabelStyle.Angle = -45;
chart.ChartAreas[0].AxisX.Interval = 1;
// Y-Axis labels settings
//chart.ChartAreas[0].AxisY.Minimum = 100;
chart.ChartAreas[0].AxisY.Minimum = 95;
// Plotting chart
using (YieldEntities context = new YieldEntities())
{
// Extract yield loss list
var yeilds = (
from yeild in context.YeildDatas
group yeild by new { yeild.Loss } into newyeild
select new
{
Loss = newyeild.Key.Loss,
Percentage = newyeild.Sum(p => p.Percentage)
}).OrderByDescending(p => p.Percentage);
//context.YeildDatas.Select(p => new { p.Loss, Percentage = p }).Distinct();
// Create new series
foreach (var yield in yeilds)
{
chart.Series.Add(yield.Loss);
chart.Series[yield.Loss].ChartType = SeriesChartType.StackedColumn100;
}
// Label settings for first series
chart.Series[0].SmartLabelStyle.Enabled = false;
chart.Series[0].LabelAngle = -90;
chart.Series[0].Font = new Font(Font.FontFamily, 15, FontStyle.Bold);
chart.Series[0].IsValueShownAsLabel = true;
var query = context.YeildDatas.ToList();
foreach (var item in query)
{
DataPoint dp = new DataPoint();
dp.SetValueXY(item.DateString, item.Percentage);
chart.Series[item.Loss].Points.Add(dp);
}
// Set empty datapoint for each series
foreach (var yield in yeilds)
{
DataPoint nulldp = new DataPoint();
nulldp.SetValueXY("", 0);
chart.Series[yield.Loss].Points.Insert(1, nulldp);
chart.Series[yield.Loss].Points.Insert(6, nulldp);
chart.Series[yield.Loss].Points.Insert(11, nulldp);
}
chart.Legends["Legend"].IsEquallySpacedItems = true;
chart.Legends["Legend"].IsTextAutoFit = true;
}
I hope to get any expert to guide me to solve this problem.

This is only sample, you can start from there:
// Data point to pixel
var pixelX = this.chart1.ChartAreas[0].AxisX.ValueToPixelPosition(dataPointX);
var pixelY = this.chart1.ChartAreas[0].AxisY.ValueToPixelPosition(dataPointY);
// Pixel to data point
var dataPointX = this.chart1.ChartAreas[0].AxisX.PixelPositionToValue(pixelX);
var dataPointY = this.chart1.ChartAreas[0].AxisY.PixelPositionToValue(pixelY);
// Use event Paint to draw your line
private void chart1_Paint(object sender, PaintEventArgs e)
{
// Convert dataPoint to pixel
var dataPointX = this.chart1.Series[0].Points[0].XValue;
var dataPointY = this.chart1.Series[0].Points[0].YValues[0];
var pixelX = this.chart1.ChartAreas[0].AxisX.ValueToPixelPosition(dataPointX);
var pixelY = this.chart1.ChartAreas[0].AxisY.ValueToPixelPosition(dataPointY);
// Only sample, pen should be initialized outside Paint method
Pen pen = new Pen(Brushes.Red);
// Example of drawing line with width=100 pixel
e.Graphics.DrawLine(pen, pixelX, pixelY, pixelX + 100, pixelY);
}

Related

Draw all the lines object in list, adding,deleting new line after that?

I save some line object in a list and try to draw them once over a picture box, but every time it draw only one line at the middle of the picture box , is there any solution for this problem.
Thanks in advance ...
public class Lines
{
public System.Drawing.Point startPoint = new System.Drawing.Point();
public System.Drawing.Point endPoint = new System.Drawing.Point();
}
Lines b = new Lines();
List<Lines> alllines = new List<Lines>();
//------------inside button click i wrote the following code----------
b.startPoint.X = rectlist[i].X;
b.startPoint.Y = (rectlist[i].Y + rectlist[i].Bottom) / 2;
b.endPoint.X = rectlist[i].Right;
b.endPoint.Y = (rectlist[i].Top + rectlist[i].Bottom) / 2;
alllines.Add(b);
this.OrignalimgPIcBX.Invalidate();
and inside the paint event of the picture box i wrote this code
Graphics g = e.Graphics;
using (var pen = new Pen(Color.Black, 2))
{
foreach (var lines in alllines)
{
g.DrawLine(pen, lines);
}
}
what is the problem ??!
the list of line in now correct
but know the line objects does not drawn in the correct position
i make the picture box size mode as stretch image , is that make any change!
this is the paint event
private void OrignalimgPIcBX_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
using (var pen = new Pen(Color.Black, 2))
{
foreach (var lines in alllines)
{
g.DrawLine(pen, lines.startPoint, lines.endPoint);
}
}
}
The is only 3 possible explanations,
All your lines are the same
All but one line is drawing out of the view-able area
Or you are eating up an exception
You need to use your debugger and break point the lines to make sure they are drawing with the correct metrics
Update
for (int i = 0; i < rectlist.Count; i++)
{
var b = new Lines(); // <-- you need to do this
b.startPoint.X = rectlist[i].X;
b.startPoint.Y = (rectlist[i].Y + rectlist[i].Bottom) / 2;
b.endPoint.X = rectlist[i].Right;
b.endPoint.Y = (rectlist[i].Top + rectlist[i].Bottom) / 2;
alllines.Add(b);
}
Your problem is you are just changing the same line and adding it to the list
I.e your list ends up with the same line over and over again

MS chart candlestick How to set tail colors

I am currently developing a candlestick chart with mschart in visual C#.
I have now created two charts and created the charts as follows
Question 1. View the Candlestick Chart at the top. I would like to apply the tail color of each rod as red or blue.
Question 2. View the bar chart at the bottom. I would like to apply Red or Blue color to this chart. I want to apply the same color to the top of the Candlestick chart. How can I do it ?
[source]
DataTable table_ChartData = new DataTable();
table_ChartData.Columns.Add("Id");
table_ChartData.Columns.Add("Open");
table_ChartData.Columns.Add("Close");
table_ChartData.Columns.Add("High");
table_ChartData.Columns.Add("Low");
table_ChartData.Columns.Add("Day");
dataGridView1.DataSource = table_ChartData;
chart1.ChartAreas["ChartArea1"].AxisX.MajorGrid.LineWidth = 1;
chart1.ChartAreas["ChartArea1"].AxisY.MajorGrid.LineWidth = 1;
chart1.ChartAreas["ChartArea1"].AxisY.Maximum = max;
chart1.ChartAreas["ChartArea1"].AxisY.Minimum = min;
chart1.ChartAreas["ChartArea1"].AxisX.LabelAutoFitStyle = LabelAutoFitStyles.WordWrap;
chart1.ChartAreas["ChartArea1"].AxisX.IsLabelAutoFit = true;
chart1.ChartAreas["ChartArea1"].AxisX.LabelStyle.Enabled = true;
chart1.Series["Candle"].XValueMember = "Day";
chart1.Series["Candle"].YValueMembers = "High,Low,Open,Close,Volume";
chart1.Series["Candle"].XValueType = System.Windows.Forms.DataVisualization.Charting.ChartValueType.Date;
chart1.Series["Candle"].CustomProperties = "PriceDownColor=Blue,PriceUpColor=Red";
chart1.Series["Candle"]["OpenCloseStyle"] = "Triangle";
chart1.Series["Candle"]["ShowOpenClose"] = "Both";
chart1.DataSource = table_ChartData;
chart1.DataBind();
////////////////////////////////////////////////////////////
chart2.ChartAreas["ChartArea1"].AxisX.MajorGrid.LineWidth = 1;
chart2.ChartAreas["ChartArea1"].AxisY.MajorGrid.LineWidth = 1;
chart2.ChartAreas["ChartArea1"].AxisY.Maximum = v_max + (v_max / 10);
chart2.ChartAreas["ChartArea1"].AxisY.Minimum = v_min / 2;
chart2.ChartAreas["ChartArea1"].AxisX.LabelAutoFitStyle = LabelAutoFitStyles.WordWrap;
chart2.ChartAreas["ChartArea1"].AxisX.IsLabelAutoFit = true;
chart2.ChartAreas["ChartArea1"].AxisX.LabelStyle.Enabled = true;
chart2.Series["Bar"].XValueMember = "Day";
chart2.Series["Bar"].YValueMembers = "Volume";
chart2.Series["Bar"].XValueType = System.Windows.Forms.DataVisualization.Charting.ChartValueType.Date;
chart2.Series["Bar"].YValueType = System.Windows.Forms.DataVisualization.Charting.ChartValueType.Int32;
chart2.DataSource = table_ChartData;
chart2.DataBind();
In a Candlestick Chart there are CustomProperties to automatiaclly set the colors of the boxes, depending on the trend:
someSeries.SetCustomProperty("PriceUpColor", "Green");
someSeries.SetCustomProperty("PriceDownColor", "Red");
Unfortunately there is no way to set the colors of the lines that connect the high- and low-values.
But, unless you have messed with other Custom attributes and if the x-values are meaningful, you can easily draw those lines, and by drawing the top and the bottom part separately you can also use different colors.
Here is an example:
private void chart6_PostPaint(object sender, ChartPaintEventArgs e)
{
ChartArea ca = chart6.ChartAreas[0];
Series s = chart6.Series[0];
Pen hiPen = Pens.Green;
Pen loPen = Pens.Red;
if (e.ChartElement == s)
foreach (DataPoint dp in s.Points)
{
float x = (float)ca.AxisX.ValueToPixelPosition(dp.XValue);
float y_hi = (float)ca.AxisY.ValueToPixelPosition(dp.YValues[0]);
float y_low = (float)ca.AxisY.ValueToPixelPosition(dp.YValues[1]);
float y_open = (float)ca.AxisY.ValueToPixelPosition(dp.YValues[2]);
float y_close = (float)ca.AxisY.ValueToPixelPosition(dp.YValues[3]);
e.ChartGraphics.Graphics.DrawLine(hiPen, x, y_low, x, Math.Min(y_close, y_open));
e.ChartGraphics.Graphics.DrawLine(loPen, x, y_hi, x, Math.Max(y_close, y_open));
}
}
To set Colors for the 2nd Chart's points you need to loop over the points as there Colors cannot be set in binding.
The code is simple:
void SetColors(Series candles, Series columns)
{
for (int i = 0; i < candles.Points.Count; i++)
{
DataPoint dp = candles.Points[i];
columns.Points[i].Color =
dp.YValues[2] > dp.YValues[3] ? Color.Red : Color.Green;
}
}
Call it after binding!
Result:
Note that to avoid seeing the original lines shine through we set the BorderWidth to 0:
someSeries.BorderWidth = 0;
Simple way:
priceSerie.Points[index].Color = Color.Blue;
// priceSerie.Points[index].BorderColor = Color.Magenta;

itextsharp table chart legends beneath bars

Can someone explain me how I can create chart like this one using itextsharp library.
As you can see here, this chart has attached table with legends to its bottom side. And each bar has year attached to it. I want to create something like that.
Second picture. This is what i have so far. I don't have year attached to each bar, and my legends are not beneath each bar like in top graph. If someone could guide me how to create identical graph like one on top i would be grateful. Thanks in advance!
How can I turn those labels to be displayed horizontally, and put those legends beneath years in table like one in picture number 1.
// Chart Centers By Year
var chartCentersByYear = new Chart
{
Width = 1000,
Height = 450,
RenderType = RenderType.ImageTag,
AntiAliasing = AntiAliasingStyles.Graphics,
TextAntiAliasingQuality = TextAntiAliasingQuality.High
};
chartCentersByYear.Titles.Add("Centers By Year");
chartCentersByYear.Titles[0].Font = new Font("Arial", 16f);
chartCentersByYear.Titles[0].Alignment = System.Drawing.ContentAlignment.TopLeft;
chartCentersByYear.ChartAreas.Add("");
chartCentersByYear.ChartAreas[0].AxisX.MajorGrid.Enabled = false;
chartCentersByYear.ChartAreas[0].AxisY.MajorGrid.Enabled = false;
chartCentersByYear.Series.Add("Count");
chartCentersByYear.Series.Add("Cases");
chartCentersByYear.Series[0].ChartType = SeriesChartType.Column; //Pie
chartCentersByYear.Series[1].ChartType = SeriesChartType.StepLine; //StepLine
chartCentersByYear.Series[1].BorderDashStyle = ChartDashStyle.DashDot;
chartCentersByYear.Series[1].BorderWidth = 3;
chartCentersByYear.Series[0].Color = Color.Brown;
chartCentersByYear.Legends.Add("1");
chartCentersByYear.Legends.Add("2");
chartCentersByYear.Legends[0].HeaderSeparator = LegendSeparatorStyle.Line;
chartCentersByYear.Legends[0].HeaderSeparatorColor = Color.Black;
chartCentersByYear.Legends[0].ItemColumnSeparator = LegendSeparatorStyle.Line;
chartCentersByYear.Legends[0].ItemColumnSeparatorColor = Color.Black;
chartCentersByYear.Legends[1].HeaderSeparator = LegendSeparatorStyle.Line;
chartCentersByYear.Legends[1].HeaderSeparatorColor = Color.Black;
chartCentersByYear.Legends[1].ItemColumnSeparator = LegendSeparatorStyle.Line;
chartCentersByYear.Legends[1].ItemColumnSeparatorColor = Color.Black;
//For the Legend
LegendCellColumn firstColumn = new LegendCellColumn();
firstColumn.ColumnType = LegendCellColumnType.SeriesSymbol;
firstColumn.HeaderBackColor = Color.WhiteSmoke;
chartCentersByYear.Legends[0].CellColumns.Add(firstColumn);
chartCentersByYear.Legends[1].CellColumns.Add(firstColumn);
LegendCellColumn secondColumn = new LegendCellColumn();
secondColumn.ColumnType = LegendCellColumnType.Text;
secondColumn.Text = "#LEGENDTEXT";
secondColumn.HeaderBackColor = Color.WhiteSmoke;
LegendItem newItemCount = new LegendItem();
newItemCount.Cells.Add(LegendCellType.Text, "Count", System.Drawing.ContentAlignment.MiddleCenter);
newItemCount.BorderWidth = 1;
newItemCount.BorderDashStyle = ChartDashStyle.Solid;
LegendItem newItemCases = new LegendItem();
newItemCases.Cells.Add(LegendCellType.Text, "Cases", System.Drawing.ContentAlignment.MiddleCenter);
newItemCases.BorderWidth = 1;
newItemCases.BorderDashStyle = ChartDashStyle.Solid;
// Getting data from a stored procedure
var totalCentersByYearResult = new Repository().GetTotalCentersByYear();
foreach (IGD_spInternationalReportCenterWithTots1_Result item in totalCentersByYearResult)
{
// For Series
chartCentersByYear.Series[0].Points.AddXY(item.YearEcmo, item.Count);
chartCentersByYear.Series[1].Points.AddY(item.Cases);
// For Legend
newItemCount.Cells.Add(LegendCellType.Text, item.Count.ToString(), System.Drawing.ContentAlignment.MiddleCenter);
newItemCases.Cells.Add(LegendCellType.Text, item.Cases.ToString(), System.Drawing.ContentAlignment.MiddleCenter);
}
chartCentersByYear.Legends[0].CustomItems.Add(newItemCount);
chartCentersByYear.Legends[0].CustomItems.Add(newItemCases);
chartCentersByYear.Legends[0].Docking = Docking.Bottom;
chartCentersByYear.Legends[1].Docking = Docking.Bottom; //Top
chartCentersByYear.Series[0].YAxisType = AxisType.Primary;
chartCentersByYear.Series[1].YAxisType = AxisType.Secondary;
//For two coordinate systems
chartCentersByYear.ChartAreas[0].AxisY2.LineColor = Color.Transparent;
chartCentersByYear.ChartAreas[0].AxisY2.MajorGrid.Enabled = false;
chartCentersByYear.ChartAreas[0].AxisY2.Enabled = AxisEnabled.True;
chartCentersByYear.ChartAreas[0].AxisY2.IsStartedFromZero = chartCentersByYear.ChartAreas[0].AxisY.IsStartedFromZero;
using (var chartimage = new MemoryStream())
{
chartCentersByYear.SaveImage(chartimage, ChartImageFormat.Png);
Byte[] newChart = chartimage.GetBuffer(); //return chartimage.GetBuffer();
var image = Image.GetInstance(newChart); //Image.GetInstance(Chart());
image.ScalePercent(50f);
image.SetAbsolutePosition(document.LeftMargin + 40, document.BottomMargin + 100);
document.Add(image);
}

How can i change my chart to look like column gradient fill style?

This is what I did in the constructor:
drawPoints.Clear();
paintToCalaculate = false;
chart1.Invalidate();
Series S0 = chart1.Series[0];
S0.ChartType = SeriesChartType.Column;
S0.IsValueShownAsLabel = true;
chart1.Series["Series1"]["PixelPointWidth"] = "0.6";
chart1.Series["Series1"]["DrawingStyle"] = "Cylinder";
S0.Color = Color.Transparent;
S0.LegendText = "";
area.BackColor = Color.White;
area.BackSecondaryColor = Color.LightSteelBlue;
area.BackGradientStyle = GradientStyle.DiagonalRight;
area = chart1.ChartAreas[0];
chart1.Series["Series1"].Points.AddXY(10, 10);
area.AxisX.Minimum = 1;
area.AxisX.Maximum = 30;
area.AxisY.Minimum = 1;
area.AxisY.Maximum = 120;
area.AxisX.MajorGrid.LineColor = Color.LightSlateGray;
area.AxisY.MajorGrid.LineColor = Color.LightSlateGray;
The problem is that I'm using the chart paint event to draw points and lines:
Pen pen = new Pen(Color.Blue, 2.5f);
SolidBrush myBrush = new SolidBrush(Color.Red);
private void chart1_Paint(object sender, PaintEventArgs e)
{
if (paintToCalaculate)
{
Series s = chart1.Series.FindByName("dummy");
if (s == null) s = chart1.Series.Add("dummy");
drawPoints.Clear();
s.Points.Clear();
foreach (PointF p in valuePoints)
{
s.Points.AddXY(p.X, p.Y);
DataPoint pt = s.Points[0];
double x = chart1.ChartAreas[0].AxisX.ValueToPixelPosition(pt.XValue);
double y = chart1.ChartAreas[0].AxisY.ValueToPixelPosition(pt.YValues[0]);
drawPoints.Add(new Point((int)x, (int)y));
s.Points.Clear();
}
paintToCalaculate = false;
chart1.Series.Remove(s);
}
foreach (Point p in drawPoints)
{
e.Graphics.FillEllipse(Brushes.Red, p.X - 2, p.Y - 2, 4, 4);
}
if (drawPoints.Count > 1)
{
e.Graphics.DrawLines(pen, drawPoints.ToArray());
}
}
I wanted it to look like this style:
It should look like in the example. But since I'm using the paint event maybe it's not possible this way.
Edit tried this solution i added a new chart control for the test in the form1 designer called chart2. Then in the constructor i did:
chart2.Titles.Add(("Introducing Chart Controls"));
ChartArea chartarea2 = new ChartArea("Main");
chart2.ChartAreas.Add(chartarea2);
Series seriesColumns = new Series("RandomColumns");
seriesColumns.Color = Color.Blue;
chart2.Series.Add(seriesColumns);
Random rnd = new Random(10);
for (int i = 0; i < 10; i++)
{
seriesColumns.Points.Add((rnd.Next(100)));
}
DataPoint dp = new DataPoint(seriesColumns);
dp.Color = Color.Red;
dp.BackGradientStyle = System.Windows.Forms.DataVisualization.Charting.GradientStyle.LeftRight;
seriesColumns.Points.Add(dp);
But no fade in/out color change. Nothing happen.
This is how chart2 look like:
To get the gradient effect you don't necessarily have to use the Paint event. The DataPoint has the BackGradientStyle property that will generate the gradient automatically for you. It will use the colour of the DataPoint. You set it like in the sample below:
var dp = new DataPoint(8D, 12D);
dp.Color = Color.Red;
dp.BackGradientStyle = System.Windows.Forms.DataVisualization.Charting.GradientStyle.LeftRight;
var series = this.chart1.Series[0];
series.Points.Add(dp);
Here the gradient is fading out. If you want it to pass into another color set the BackSecondaryColor property.
dp.BackSecondaryColor = Color.Green;

MapPolyline not being drawn

I am trying to use a MapPolyLine in my Map to show a real-time route, hopefully it will move/scale this time. The thing is the line is not being shown on the map, and I cannot find any programming mistake:
C#
MapLayer pathLayer;
//Constructor
pathLayer = new MapLayer();
MapPolyline line = new MapPolyline();
line.StrokeColor = Colors.Red;
line.StrokeThickness = 10;
//line.Path.Add(several points); Tested, no effect
MapOverlay overlay = new MapOverlay();
overlay.Content = line;
//overlay.GeoCoordinate = new GeoCoordinate(0,0); Tested, no effect
//overlay.PositionOrigin = new Point(0.0, 1.0); Tested, no effect
pathLayer.Add(overlay);
MyMap.Layers.Add(pathLayer);
void geolocator_PositionChanged(Geolocator sender, PositionChangedEventArgs args)
{
MapPolyline line = pathLayer.First(TrackPath).Content as MapPolyline;
line.Path.Add(args.Position.Coordinate); // Checked values, line.Path adds them correctly
}
EDIT: New info. The emulator shows an error when trying to add it using XAML, and the emulator shows the name of the class on the top of the map as a graphic glitch:
MapPolylines and MapPolygons should be added to the MapElements collection... not a MapLayer or a MapOverlay.
You should be able to make this example work for you.
MapPolyline line = new MapPolyline();
line.StrokeColor = Colors.Red;
line.StrokeThickness = 10;
line.Path.Add(new GeoCoordinate(47.6602, -122.098358));
line.Path.Add(new GeoCoordinate(47.561482, -122.071544));
MyMap.MapElements.Add(line);
In your GeoCoord watcher you'll have to get the line from the map's MapElements collection, and add the new position to the line's path instead of predefining like I did. This should be doable.
In Windows Phone 8.1 try add points in this way. "punkty" is my collection.
List<BasicGeoposition> PosList = new List<BasicGeoposition>();
foreach (var item in punkty)
{
PosList.Add(new BasicGeoposition()
{
Latitude = item.Position.Latitude,
Longitude = item.Position.Longitude
});
}
//Example of one point
//PosList.Add(new BasicGeoposition()
//{
// Latitude = 52.46479093,
// Longitude = 16.91743341
//});
MapPolyline line = new MapPolyline();
line.StrokeColor = Colors.Red;
line.StrokeThickness = 5;
line.Path = new Geopath(PosList);
myMap.MapElements.Add(line);

Categories