I am using an MSChart (System.Web.UI.DataVisualization.Charting.Chart()) Line Chart in an ASP.NET web page. It works fine except that the X-Axis intervals are off by 1 and I cannot figure out how to make they align to the right numbers. Here it is:
Notice the X-Axis (MPH) numbers: -1, 9, 19, 29....
Those are supposed to be 0, 10, 20, 30. I have taken measure in the code that I thought should have made it the way I want, but nothing works. My chart is 100% built in the c#, nothing in the ASPX. Here it is:
private void BuildLineChart(string reportName, List < DataPoint > points, string xTitle, string yTitle) {
var chart = new Chart();
// Build a column series
Series series = new Series(reportName);
series.ChartType = SeriesChartType.Line;
chart.Series.Add(series);
// Define the chart area
Grid grid = new Grid();
grid.LineWidth = 0;
ChartArea chartArea = new ChartArea();
chartArea.AxisX.MajorGrid = grid;
chartArea.AxisX.Crossing = 0;
chartArea.AxisX.Interval = 10;
chartArea.AxisX.IsStartedFromZero = true;
if (xTitle != string.Empty) {
chartArea.AxisX.Title = xTitle;
chartArea.AxisX.TitleAlignment = StringAlignment.Center;
chartArea.AxisX.TextOrientation = TextOrientation.Horizontal;
chartArea.AxisX.TitleFont = new Font("Verdana", 12);
}
if (yTitle != string.Empty) {
chartArea.AxisY.Title = yTitle;
chartArea.AxisY.TitleAlignment = StringAlignment.Center;
chartArea.AxisY.TextOrientation = TextOrientation.Rotated270;
chartArea.AxisY.TitleFont = new Font("Verdana", 12);
}
ChartArea3DStyle areaStyle = new ChartArea3DStyle(chartArea);
areaStyle.Rotation = 0;
chart.ChartAreas.Add(chartArea);
Axis xAxis = new Axis(chartArea, AxisName.X);
Axis yAxis = new Axis(chartArea, AxisName.Y);
// Set chart width and height (Note: increasing the width and height of the chart doesn't seem to improve the fidelity in the generated pdf (downstream))
chart.Width = new System.Web.UI.WebControls.Unit(800, System.Web.UI.WebControls.UnitType.Pixel);
chart.Height = new System.Web.UI.WebControls.Unit(300, System.Web.UI.WebControls.UnitType.Pixel);
// Bind the data to the chart
foreach(DataPoint point in points) {
chart.Series[reportName].Points.Add(point);
}
chart.Series[reportName].BorderWidth = 2;
//chart.Series[reportName].IsValueShownAsLabel = true;
string filename = Server.MapPath("./ChartImages") + "/" + reportName + ".png";
chart.SaveImage(filename, ChartImageFormat.Png);
}
var points = new List<DataPoint>();
points.Add(new DataPoint(0, 0));
points.Add(new DataPoint(8, 25));
points.Add(new DataPoint(9, 15));
points.Add(new DataPoint(14, 25));
points.Add(new DataPoint(15, 15));
points.Add(new DataPoint(22, 26));
points.Add(new DataPoint(23, 16));
points.Add(new DataPoint(36, 26));
points.Add(new DataPoint(36, 17));
points.Add(new DataPoint(53, 26));
points.Add(new DataPoint(53, 19));
points.Add(new DataPoint(73, 26));
BuildLineChart("GearSplit", points, "MPH", "RPM X 100");
Notice especially the Interval = 10 and the IsStartedFromZero = true.
Set the axis minimum value:
chartArea.AxisX.Minimum = 0;
Related
How can I get x,y location of graph wher X and Y Axis starts (x,y location of point 0,0 in line chart)? This location is changable depending on text values on Y Axis probably.
Image is for illustrative purpose only.
How can I change the background color (currently is linear blue) of chart (I use PDFsharp NuGet package)?
Sample code:
ChartFrame chartFrame = new ChartFrame();
chartFrame.Location = new XPoint(xOffset, yLoc);
double chartHeight = Math.Min(250, (page.Height - yLoc - 10));
chartFrame.Size = new XSize((page.Width - (xOffset * 2)), chartHeight);
Chart chart = new Chart(ChartType.Line);
//chart.PlotArea.FillFormat.Color = XColor.FromArgb(255, 255, 255, 255); //plot background???
Series series = chart.SeriesCollection.AddSeries();
series.ChartType = ChartType.Line;
series.MarkerStyle = PdfSharp.Charting.MarkerStyle.None;
series.Name = "Horizontal"; //Series 1
series.MarkerBackgroundColor = XColor.FromArgb(255, 0, 0, 255); //blue
series.MarkerSize = 1;
series.Add(line1);
series = chart.SeriesCollection.AddSeries();
series.ChartType = ChartType.Line;
series.MarkerStyle = PdfSharp.Charting.MarkerStyle.None;
series.Name = "Vertical"; //Series 2
series.MarkerBackgroundColor = XColor.FromArgb(255, 0, 170, 0); //green
series.MarkerSize = 1;
series.Add(line2);
series = chart.SeriesCollection.AddSeries();
series.ChartType = ChartType.Line;
series.MarkerStyle = PdfSharp.Charting.MarkerStyle.None;
series.Name = "3D position"; //Series 3
series.MarkerBackgroundColor = XColor.FromArgb(255, 255, 0, 0); //red
series.MarkerSize = 1;
series.Add(line3);
chart.XAxis.MajorTickMark = TickMarkType.Outside;
chart.XAxis.Title.Caption = "No. Events"; //X-Axis
chart.YAxis.MajorTickMark = TickMarkType.Outside;
chart.YAxis.HasMajorGridlines = true;
double yOffset = ((maxYscale - minYscale) / 6);
if (yOffset < 0.01f)
yOffset = 0.01f;
chart.YAxis.MinimumScale = 0;
chart.YAxis.MaximumScale = Math.Round(Math.Round((maxYscale + yOffset), 3), 2);
double jump = Math.Round(yOffset, 2);
if (jump < 0.01f)
jump = 0.01f;
chart.YAxis.MajorTick = jump;
chart.YAxis.TickLabels.Format = "#0.00";
//chart.YAxis.HasMinorGridlines = true;
chart.YAxis.Title.Caption = "[m]"; //Y-Axis
chart.YAxis.HasMajorGridlines = true;
chart.PlotArea.LineFormat.Color = XColors.DarkGray;
chart.PlotArea.LineFormat.Width = 1;
chart.PlotArea.LineFormat.Visible = true;
chart.Legend.Docking = DockingType.Bottom;
chart.Legend.LineFormat.Visible = true;
XSeries xseries = chart.XValues.AddXSeries();
xseries.Add(eventLine);
chartFrame.Add(chart);
chartFrame.Draw(gfx);
Re:for X,Y location:
I made quick check for text length on YAxis and then according to that calculate new X,Y position.
Re: Background color:
These colours are hard-coded in the ChartFrame class (Draw method). So the nugget package for PDFSharp is not good you need to download the library source code.
Search for "XColor.FromArgb(0xFFD0DEEF)" and update the source code as needed.
I would like to display the 2 different stacked area elements according to their parameters. But the chart area displays it not as specified and puts the second block at the top right corner of the first stacked area. They should be displayed side by side not stacked.
...
using System.Windows.Forms.DataVisualization.Charting;
namespace Gantt_Tool
{
public partial class ReadModel : Form
{
public ReadModel()
{
InitializeComponent();
CreateChart();
}
private void ReadModel_Load(object sender, EventArgs e)
{
}
private void CreateChart()
{
chart1.Series.Add($"a");
chart1.Series[$"a"].Points.Add(new DataPoint(0, 2));
chart1.Series[$"a"].Points.Add(new DataPoint(2, 2));
chart1.Series[$"a"].ChartType = SeriesChartType.StackedArea;
chart1.Series.Add($"b");
chart1.Series[$"b"].Points.Add(new DataPoint(2, 3));
chart1.Series[$"b"].Points.Add(new DataPoint(5, 3));
chart1.Series[$"b"].ChartType = SeriesChartType.StackedArea;
}
}
How can I set the blocks to a side by side order or placed freely? And how can I get unfilled rectangles?
Update: Here is an example of how it should look like:
From your comments, I take that you want to have a chart with freely-placed, unfilled rectangles and labels.
None of the MSChart types will do that.
Here is how to use a Point chart with a few lines of owner-drawing. Note how nicely this will behave when resizing the chart...
Here is the setup:
Axis ax = chart1.ChartAreas[0].AxisX;
Axis ay = chart1.ChartAreas[0].AxisY;
ax.Maximum = 9; // pick or calculate
ay.Maximum = 6; // minimum and..
ax.Interval = 1; // maximum values..
ay.Interval = 1; // .. needed
ax.MajorGrid.Enabled = false;
ay.MajorGrid.Enabled = false;
Series s1 = chart1.Series.Add("A");
s1.ChartType = SeriesChartType.Point;
Now we add your five boxes. I use a sepcial function that adds the points and stuffs the box size into the Tag of each point..:
AddBox(s1, 1, 0, 3, 1, "# 1");
AddBox(s1, 2, 1, 2, 2, "# 2");
AddBox(s1, 4, 0, 4, 2, "# 3");
AddBox(s1, 4, 2, 2, 2, "# 4");
AddBox(s1, 4, 4, 1, 1, "# 5");
int AddBox(Series s, float x, float y, float w, float h, string label)
{
return AddBox(s, new PointF(x, y), new SizeF(w, h), label);
}
int AddBox(Series s, PointF pt, SizeF sz, string label)
{
int i = s.Points.AddXY(pt.X, pt.Y);
s.Points[i].Tag = sz;
s.Points[i].Label = label;
s.Points[i].LabelForeColor = Color.Transparent;
s.Points[i].Color = Color.Transparent;
return i;
}
The drawing is also simple; only the use of the Axes function ValueToPixelPosition is noteworthy..:
private void chart1_PostPaint(object sender, ChartPaintEventArgs e)
{
if (chart1.Series[0].Points.Count <= 0) return;
Axis ax = chart1.ChartAreas[0].AxisX;
Axis ay = chart1.ChartAreas[0].AxisY;
Graphics g = e.ChartGraphics.Graphics;
using (StringFormat fmt = new StringFormat()
{ Alignment = StringAlignment.Center,
LineAlignment = StringAlignment.Center})
foreach (Series s in chart1.Series)
{
foreach (DataPoint dp in s.Points)
{
if (dp.Tag == null) break;
SizeF sz = (SizeF)dp.Tag;
double vx2 = dp.XValue + sz.Width;
double vy2 = dp.YValues[0] + sz.Height;
int x1 = (int)ax.ValueToPixelPosition(dp.XValue);
int y1 = (int)ay.ValueToPixelPosition(dp.YValues[0]);
int x2 = (int)ax.ValueToPixelPosition(vx2);
int y2 = (int)ay.ValueToPixelPosition(vy2);
Rectangle rect = Rectangle.FromLTRB(x1, y2, x2, y1);
using (Pen pen = new Pen(s.Color, 2f))
g.DrawRectangle(pen, rect);
g.DrawString(dp.Label, Font, Brushes.Black, rect, fmt);
}
}
}
Here is a little Linq to calculate the Minimum and Maximum values for the Axes to hold just the right size; chart won't do it by itself since the size in the tags of the points is not known...
private void setMinMax(Chart chart, ChartArea ca)
{
var allPoints = chart.Series.SelectMany(x => x.Points);
double minx = allPoints.Select(x => x.XValue).Min();
double miny = allPoints.Select(x => x.YValues[0]).Min();
double maxx = allPoints.Select(x => x.XValue + ((SizeF)x.Tag).Width).Max();
double maxy = allPoints.Select(x => x.YValues[0] + ((SizeF)x.Tag).Height).Max();
ca.AxisX.Minimum = minx;
ca.AxisX.Maximum = maxx;
ca.AxisY.Minimum = miny;
ca.AxisY.Maximum = maxy;
}
I am trying to move Main X and Y axes to the point of (0,0), like:
I tried to add 4 lines: (-10,0) to (10,0) and (0-10) to (0,10)
Series ttt = new Series("")
{
ChartType = SeriesChartType.Line,
Color = Color.Yellow,
BorderWidth = 5,
Font = new Font(Font.OriginalFontName, 20, FontStyle.Regular),
};
ttt.Points.AddXY(0,10);
//ttt.Points.AddXY(0, -10);
this.chart1.Series.Add(ttt);
Unfortunately, also the Main X and Y axis moved to -10,-10 like:
Setting Axis.Crossing does the job. But I'm not sure how to get double arrows on each axis, but the following code gives this effect
ttt.Points.AddXY(0, 10);
ttt.Points.AddXY(10, 0);
ttt.Points.AddXY(0, -10);
ttt.Points.AddXY(-10, 0);
ttt.Points.AddXY(0, 10);
this.chart1.Series.Add(ttt);
chart1.ChartAreas[0].AxisX.Crossing = 0; // <--- These two lines
chart1.ChartAreas[0].AxisY.Crossing = 0;
And you probably also want to set the axis min/max by
chart1.ChartAreas[0].AxisX.Maximum = 15;
chart1.ChartAreas[0].AxisX.Minimum = -15;
chart1.ChartAreas[0].AxisY.Maximum = 15;
chart1.ChartAreas[0].AxisY.Minimum = -15;
I am using netDXF (https://netdxf.codeplex.com/) to generate a DXF file for use with AutoCAD. However, I have an issue with getting the width of MText correct. I want to be able to define a width that the text should fit into, and change the width factor of the text (squash it horizontally) so that it fits in the defined area. So if I have a 40mm width to fit the text into and the text is 80mm long, it needs to have a width factor of 0.5. The only problem is that I don't know how to accurately determine the width of the text. I have tried the following methods and was unsuccessful in getting the correct result:
Why is Graphics.MeasureString() returning a higher than expected number?
Measure a String without using a Graphics object?
http://www.codeproject.com/Articles/2118/Bypass-Graphics-MeasureString-limitations
I have attached my code. I am basically printing a horizontal line using each of the 3 methods to calculate text width and comparing it to the actual text width. If I change the font, I get varying results. I have attached two images. One using the code with Calibri and one with Arial. I need the line to be on the edges of the text no matter what font I use.
Here is my code:
public void TestMethod1()
{
Application.SetCompatibleTextRenderingDefault(false);
//text width in mm
float textWidth = 40;
float textHeight = 200;
string labelText = "HELLO WORLD!";
TextStyle textStyle = new TextStyle("Calibri");
DxfDocument dxf = new DxfDocument();
Layer layer1 = new Layer("layer1");
layer1.Color = new AciColor(0, 0, 255);
layer1.Name = "Text";
MText text1 = new MText(new Vector2(0, 0), textHeight, 0, textStyle);
text1.Layer = layer1;
text1.AttachmentPoint = MTextAttachmentPoint.MiddleCenter;
//Will the text fit in the bounds of the rectangle? If not change width factor so it does.
Font f = new Font(textStyle.FontName, textHeight);
Size size = TextRenderer.MeasureText(labelText, f);
SizeF sizeF = graphicsMeasureString(labelText, f);
int width = MeasureDisplayStringWidth(labelText, f);
float widthFactor = Math.Min(1, textWidth / sizeF.Width);
MTextFormattingOptions mtextOptions = new MTextFormattingOptions(text1.Style);
//mtextOptions.WidthFactor = widthFactor;
text1.Write(labelText, mtextOptions);
//Red, g.MeasureString
Line line1 = new Line(new Vector2(0 - sizeF.Width / 2, 0), new Vector2(0 + sizeF.Width / 2, 0));
line1.Color = new AciColor(255, 0, 0);
//Green, TextRenderer
Line line2 = new Line(new Vector2(0 - size.Width / 2, 5), new Vector2(0 + size.Width / 2, 5));
line2.Color = new AciColor(0, 255, 0);
//Yellow, MeasureDisplayStringWidth
Line line3 = new Line(new Vector2(0 - width / 2, -5), new Vector2(0 + width / 2, -5));
line3.Color = new AciColor(255, 255, 0);
dxf.AddEntity(text1);
dxf.AddEntity(line1);
dxf.AddEntity(line2);
dxf.AddEntity(line3);
dxf.Save("Text Width Test.dxf");
}
public SizeF graphicsMeasureString(string text, Font f)
{
Bitmap fakeImage = new Bitmap(1, 1);
Graphics g = Graphics.FromImage(fakeImage);
SizeF sizeF = g.MeasureString(text, f, new PointF(100, 0), StringFormat.GenericTypographic);
return sizeF;
}
public int MeasureDisplayStringWidth(string text, Font f)
{
Size size = TextRenderer.MeasureText(text, f);
Bitmap fakeImage = new Bitmap(1, 1);
Graphics g = Graphics.FromImage(fakeImage);
System.Drawing.StringFormat format = new System.Drawing.StringFormat();
System.Drawing.RectangleF rect = new System.Drawing.RectangleF(0, 0, 1000, 1000);
System.Drawing.CharacterRange[] ranges = { new System.Drawing.CharacterRange(0, text.Length) };
System.Drawing.Region[] regions = new System.Drawing.Region[1];
format.SetMeasurableCharacterRanges(ranges);
regions = g.MeasureCharacterRanges(text, f, rect, format);
rect = regions[0].GetBounds(g);
return (int)(rect.Right + 1.0f);
}
I'm in the process of making my own Captcha check on my website.
Everything's working, except I need some blurryness/effects on my text that's not viewable by a webcrawler etc.
Some of the code used to generate the text on the image:
Bitmap BitMap = new Bitmap(#"C:\Users\Public\Pictures\Sample Pictures\Desert.jpg");
Graphics g = Graphics.FromImage(BitMap);
g.DrawString(""+RandomNumberString+"", new Font("Tahoma", 40), Brushes.Khaki, new PointF(1, 1));
pictureBox1.Image = BitMap;
Example:
What can I do to get my effects/blurryness on my text?
Thank you!
Why roll out your own captcha when reCAPTCHA is free, accessible (through the audio option, making it usable for people with visual issues) and at the same time helps digitize various publications? There's even a .NET implementation.
Edit:
Seeing how it's for fun, having a look at "An ASP.NET Framework for Human Interactive Proofs" might give you some good ideas. Especially the ImageHipChallenge as it includes image distortion code examples.
For example:
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
int newX = (int)(x + (distortion * Math.Sin(Math.PI * y / 64.0)));
int newY = (int)(y + (distortion * Math.Cos(Math.PI * x / 64.0)));
if (newX < 0 || newX >= width) newX = 0;
if (newY < 0 || newY >= height) newY = 0;
b.SetPixel(x, y, copy.GetPixel(newX, newY));
}
}
Which will move the pixels in a wave like fashion. Such as in the second word of your example.
Have a look at this tutorial. There you will find a code example on how to create a CAPTCHA using C# and the DrawString method.
Hope, this helps.
I've used this for about 5 years and it doesn't involve any integration with horrid 3rd party APIs.
http://www.codeproject.com/KB/aspnet/CaptchaImage.aspx
protected void Page_Load(object sender, EventArgs e)
{
if(! IsPostBack)
{
LoadCaptcha();[![enter image description here][1]][1]
}
}
public void LoadCaptcha()
{
try
{
Bitmap objBitmap = new Bitmap(130, 60);
Graphics objGraphics = Graphics.FromImage(objBitmap);
objGraphics.Clear(Color.White);
Random objRandom = new Random();
objGraphics.DrawLine(Pens.Black, objRandom.Next(0, 50), objRandom.Next(10, 30), objRandom.Next(0, 200), objRandom.Next(0, 50));
objGraphics.DrawRectangle(Pens.Blue, objRandom.Next(0, 20), objRandom.Next(0, 20), objRandom.Next(50, 80), objRandom.Next(0, 20));
objGraphics.DrawLine(Pens.Blue, objRandom.Next(0, 20), objRandom.Next(10, 50), objRandom.Next(100, 200), objRandom.Next(0, 80));
Brush objBrush =
default(Brush);
//create background style
HatchStyle[] aHatchStyles = new HatchStyle[]
{
HatchStyle.BackwardDiagonal, HatchStyle.Cross, HatchStyle.DashedDownwardDiagonal, HatchStyle.DashedHorizontal, HatchStyle.DashedUpwardDiagonal, HatchStyle.DashedVertical,
HatchStyle.DiagonalBrick, HatchStyle.DiagonalCross, HatchStyle.Divot, HatchStyle.DottedDiamond, HatchStyle.DottedGrid, HatchStyle.ForwardDiagonal, HatchStyle.Horizontal,
HatchStyle.HorizontalBrick, HatchStyle.LargeCheckerBoard, HatchStyle.LargeConfetti, HatchStyle.LargeGrid, HatchStyle.LightDownwardDiagonal, HatchStyle.LightHorizontal
};
////create rectangular area
RectangleF oRectangleF = new RectangleF(0, 0, 300, 300);
objBrush = new HatchBrush(aHatchStyles[objRandom.Next(aHatchStyles.Length - 3)], Color.FromArgb((objRandom.Next(100, 255)), (objRandom.Next(100, 255)), (objRandom.Next(100, 255))), Color.White);
objGraphics.FillRectangle(objBrush, oRectangleF);
//Generate the image for captcha
string captchaText = string.Format("{0:X}", objRandom.Next(1000000, 9999999));
//add the captcha value in session
Session["CaptchaVerify"] = captchaText;
Font objFont = new Font("Courier New", 15, FontStyle.Bold);
//Draw the image for captcha
objGraphics.DrawString(captchaText, objFont, Brushes.Black, 20, 20);
// objBitmap.Save(HttpContext.Current.Response.OutputStream, ImageFormat.Gif);
byte[] _bytes;
using (MemoryStream ms = new MemoryStream())
{
objBitmap.Save(ms, ImageFormat.Bmp);
_bytes = ms.ToArray();
}
imgcaptcha.ImageUrl = "data:image;base64," + Convert.ToBase64String(_bytes);
ImageCapchaSubmit.ImageUrl = "data:image;base64," + Convert.ToBase64String(_bytes);
}
catch (Exception)
{
}
}