I'm not sure if I titled the question correctly so it would be better if I explained what I'm trying to do. I want to add some images on chart control and
around their to draw graphics.
I want to display the layout of the sensors on the coordinate plane defined by coordinates, while noting the location of geographic objects (forest, river, etc.). These objects will be images which I want to add to the chart/
How can I do it? It is possible?
If you show us an example we may be able to help to find the best way.
There are several options.:
You can place image controls like PictureBox or Panel on the Chart by adding them to the chart's Controls collection
You can draw them in the Pre- or PostPaint event
You can assemble a BackImage that contains all the Images you want to place around the chart.
You can add ImageAnnotations to the chart. (Recommended)
The latter obviously is the one that is best integrated.
Here is an example:
We start by adding the images we want to use to the Chart's Images collection:
List<string> imgFiles = new List<string>()
{ yourImageFileName1, ...};
for (int i = 0; i < imgFiles.Count; i++)
{
Image img = Bitmap.FromFile(imgFiles[i]);
chart1.Images.Add(new NamedImage("Image" + i, img));
}
Note the NamedImage class used here. It allows you to refer to the images by a string; pick better names! Maybe Path.GetFileNameWithoutExtension(imgFiles[i]) - Also note that you must not Dispose of the Images or else they will disappear!
Next make make a little room at the right side of the chart by reducing the ChartArea's size:
ChartArea ca = chart1.ChartAreas[0];
ca.Position = new ElementPosition(5,5,70,90);
Note the values are percentages of the Chart's ClientSize, so they will grow and shrink when resizing the Chart!
Finally we can add them all. You will want to add them at specific positions. I add them at some space to the right and also make them moveable:
foreach (NamedImage img in chart1.Images)
{
ImageAnnotation ia = new ImageAnnotation();
ia.Image = img.Name;
ia.AllowMoving = true;
ia.X = 77;
ia.Y = 15 * chart1.Images.IndexOf(img) + 5;
chart1.Annotations.Add(ia);
}
Now you should see the Annotions. And if you add this event:
private void chart1_AnnotationPositionChanging(object sender,
AnnotationPositionChangingEventArgs e)
{
testLabel.Text = e.Annotation.X + " " + e.Annotation.Y;
}
..you will see just what the numbers for the best position are. Eventually you will not keep them moveable, of course..
Note the the Annotations' position is also in percentages, so they will move along nicely, when the chart get resized! You may also scale the Images by setting the Width and Height; this is a little tricky, as it will also be in percent (and not as the docs falsely state in pixels). You would probably want to loop over the ImageAnnotations and rescale them in the Resize event..: ia.Height = ia.Width * chart1.Width / chart1.Height;
Also note that there are other ways to position annotations, like anchored to datapoints, but this seem the best for a static adornment.
Related
I have a scenario in which I want to display numerous images in a panel using only horizontal scroll.
To enable only Horizontal Scroll only, I used the following code in the constructor
public Form()
{
InitializeComponent();
panelImageGallery.AutoScroll = true;
panelImageGallery.VerticalScroll.Enabled = false;
panelImageGallery.VerticalScroll.Visible = false;
}
And then to display and use images in the panel I wrote the following lines of code
int increment = 0;
lblCount.Text = "0/" + files.Length;
for (int i=0;i<files.Length;i++)
{
PanelPictureBox box = new PanelPictureBox();
box.IMAGE= files[i];
box.Location = new Point(panelImageGallery.Location.X + increment, panelImageGallery.Location.Y+10);
box.INDEX = i+1;
panelImageGallery.Controls.Add(box);
increment += 300;
}
Following is the result, and it can be seen that although, I have 350 images but NOT all images are in the panel as there are only 109 images and even both the horizontal and vertical scrolls are there.
Also, when I scrolled the panel all the way to the end then there are some display issues at the end too as the last image gets joined with the second last image.
Another thing that I observed was that when I increased the margin between the images, then fewer and fewer images got displayed inside the panel. So for example, when I set the increment to 500 then only 66 images got displayed in the panel instead of all the 350 images. My feeling is that there could be restriction on the maximum size of the panel, So what is actually happening here and how to resolve this issue ?
This is a limitation because of some of the Windows messages, for example as mentioned in documentations:
Note that the WM_HSCROLL message carries only 16 bits of scroll box
position data. Thus, applications that rely solely on WM_HSCROLL (and
WM_VSCROLL) for scroll position data have a practical maximum position
value of 65,535.
In general, it's not a good idea to try to host too many controls on the form.
In your case, you may want to try controls which performs automatic layout, like FlowLayoutPanel, TableLayoutPanel, Docking into the left of a panel or decrease the margin between your controls.
Or as a proper fix, you can rely on ListView control which support virtual mode, or implement a custom drawn control, or use paging to show a limited number of controls in each page.
I have a C# 4.7 WinForms application with line chart. I want to save chart image with very large resolution e.g 10000x2000. Here is what I have archived:
Form1.Width = 3860; //my screen width resolution - maximum allowed
Form1.Height = 1080; //my screen height resolution - maximum allowed
chart1.Dock = DockStyle.Fill;
chart1.SaveImage(saveFile.FileName, ChartImageFormat.Jpeg);
That code saves jpeg image with 3860x1080 resolution and i can zoom in points I need. But it is not enough and I want to enlarge my form or chart to
get an image like 10000x2000, VS says it cannot be larger than my screen.
I have a lot of data on this chart, so bigger form provides me with more data on a chart. Picture is required for other people to see the maximum info.
How can I do it?
This is quite simple.
All you need to do is create the Chart in code without displaying it on screen.
Now you can size it as needed..:
Chart bigChart = new Chart();
bigChart.ChartAreas.Add("ca");
Series s1 = bigChart.Series.Add("s1");
s1.BorderWidth = 5;
s1.ChartType = SeriesChartType.Line;
// testdata
Random rnd = new Random(8);
for (int i = 0; i < 111; i++)
{
s1.Points.AddXY(rnd.Next(123) + i, rnd.Next(12 * i)) ;
}
bigChart.Size = new Size(10000, 8000);
bigChart.SaveImage(filename, ChartImageFormat.Png);
A few notes:
You may want to add a Legend as well, as the dynamically created Chart has none of the default elements..
If you want to you can use a chart you have, serialize its content to a file (or stream) and then load (deserialize) it into the big chart..
The automatic smartness of MSChart tends to hit some limit when the sizes grow a lot; so brace yourself for tweaking some settings for line width, Fonts etc..
You also may want to load the saved image and set the dpi to a bigger values than the default, which is the screen resolution..
Final remark: Sometimes it is better to save the chart as a vector format; these are also provided in the SaveImage method.
Update: Maybe you want to combine vector and pixel formats as in Jimi's last comments.
You may want to study this post which discusses a few general options.
I am currently working on a module to create charts to display data.
I use System.Windows.Forms.DataVisualization.Charting.Chart.
I have two striplines showing the the average result we got and another one showing what we want.
So far I was really happy with what I had but I want to add explicit arrow to point these lines. And I can't figure out how to do it.
I saw that Line Annotation might be of help but I couldn't find a way to do what I wanted.
Here is an example of what I would like to do :
You have a choice of using
Annotations or
GDI+ drawing
In both cases the challenge is to get the positions right.
The more natural way to go is using Annotations, so let's look at this first:
There are various types but different capabilities; text can be displayed by RectangleAnnotation or a TextAnnotation. Lines and arrowheads can only be displayed by LineAnnotations. So we need a pair of Line- plus TextAnnotation for each of your two lines.
Like many other chart elements annotations are positioned in percentages of their respective containers; this makes things rather tricky at times.
To place a line annotation all to the right of the chart you could set its X property to 100; to let it go to the left you set the width to a negative number. The problems are starting after that..
To find out where the right edge of the ChartArea is you need to code the Pre- or PostPaint event and use the ToRectangleF method.
To find out the y-value you will want to calculate it from a data value; for this you can use the AxisY.ValueToPixelPosition method, which converts to pixels, from which you can calculate the percantage using the chart's ClientArea along with the ChartArea percentage size.
Complicated? Yup. Annotations get a lot simpler to use if you can anchor them to a certain DataPoint; but yours are outside the ChartArea..
Here is a function that should help when doing the calculations:
double PercentFromValue(Chart chart, ChartArea ca, double value)
{
Axis ay = ca.AxisY;
RectangleF car = ca.Position.ToRectangleF();
double py = ay.ValueToPixelPosition(value);
int caHeight = (int)(chart.ClientRectangle.Height * car.Height / 100f);
return 100d * py / caHeight;
}
Note that it will only work reliably when called from of of the Pre/PostPaint events..
So this is an example of a PrePaint event that positions a LineAnnotation lAnn:
private void chart1_PrePaint(object sender, ChartPaintEventArgs e)
{
Rectangle cr = chart1.ClientRectangle;
ChartArea ca = chart1.ChartAreas[0];
RectangleF car = ca.Position.ToRectangleF();
lAnn.Width = car.Width - lAnn.X;
lAnn.Y = PercentFromValue(chart1, ca, someDataValue);
}
When you insert a valid DataPoint YValue the starting y-position will be set. You can play with it until you find a nice combination of setting the four position properties..
When creating and adding the four(!) annotations you may want to keep class level references, so you won't have to refer to them from the Annotations collection..
For the LineAnnotation you will want to set the linewidth, color and the capstyle, either using the EndCap or the StartCap:
lAnn.EndCap = LineAnchorCapStyle.Arrow;
GDI+ drawing is more straight-forward, provided you know where you want to draw the lines and the text.
It is also done in the PrePaint event, again using the ValueToPixelPosition to find the pixelposition of the two data lines.. Other than that is all the usual stuff with Graphics.DrawLine, a Pen with and Start- or EndCap and Graphics.DrawString or maybe TextRenderer.DrawText..
I'm frankly not sure which way I would choose..
create triangle image and set the marker image as:
Chart1.Series.Points.AddXY(0, 10);
Chart1.Series.Points.AddXY(20,10);
Chart1.Series[0].Points[1].MarkerImage = "TriangleImage.bmp";
Chart1.Series[0].Points[1].MarkerImageTransparentColor = Color.White;
or
Chart1.Series[0].Points[1].MarkerStyle = MarkerStyle.Triangle;
I'm doing some programming in WPF. My goal is creat a TRPG(Text-RPG), so a map is definitely needed.
I got a PNG picture that can be separated in 5×5.
I want to separate this PNG, the add some Code on these little squares, for example, the picture's code in left bottom corner is (0, 0).
(I'll add functions to make sure I'll be able to travel on this MAP later.)
Now I want to figure out how can I separate the PNG?
Then add it in my code?
Creat a new .cs? .edmx?
I'd recommend taking a step back and concisely identify exactly how you plan to represent geographic area in your application.
If you are planning to have an NxM grid to display geographic area of the "map" in your app, you might want to instead just draw the map's "image" on the background of your window. You can then overlay transparent controls on top of the actual map (such as labels for the individual grid cells).
Fortunately, WPF provides a very applicable layout mechanism for you to do this, a Grid.
See http://wpftutorial.net/GridLayout.html.
You can create a dynamic bitmap array. and define all of the bitmaps and just run a foreach loop to draw them.
Bitmap[] bitmapArray = new Bitmap[width * height]; //the size of your map
for(int i = 0; i <= bitmapArray.Length - 1; i++)
{
//Define the bitmaps here
}
foreach(Bitmap b in bitmapArray)
{
//Drawing Code
}
I have a WinForms empty panel, and I'm adding images dynamically to this panel.
To center the first image added, I just:
Get the width (WP) of the panel and divide by 2;
Get the width (WI) of the image and divide by 2;
WP - WI = Left position of image X;
But I can't figure out some dinamically way to set this position when I have more than one image. Is there a way to calculate this X ? Am I right about this? Is there a easier way?
Thanks.
Well if you have only one row, some where you can have a collection of Image objects, say
List<Image> images.
And method
void PositionImages()
{
int totalWidth = images.Sum(img=>img.Width);
int startX = (panel.Width - totalWidth)/2;
}
Should work to you, but you will need to check it. For example I suppose here that tolalWidth of all images in collection is always less then panel.Width.