I'm trying to add a Chart control dynamically to a Form (using C#, this should all be .NET 4.0), but it's always blank (only the background color shows). I tried the same code in a Form that already has a control and it works, so I imagine it's some initialization function I should call (I tried Invalidate() on the control and Refresh() on both control and the panel it's being placed in, made no difference). I went through the few similar posts I found, tried throwing in any other commands they used (BeginInit() is from one such post) but no luck so far. Any ideas?
BTW I want to display 6-9 charts (position, speed and acceleration in 3D space) so I'd rather add them dynamically than have 9 sets of assignments. Here's the code that adds the charts to the panel:
foreach (KeyValuePair<string, List<double>> p in b.storedValues)
{
Control c = getChartForData(p);
panel1.Controls.Add(c);
c.Invalidate();
c.Refresh();
break;
}
And the function that creates each chart:
private Chart getChartForData(KeyValuePair<string, List<double>> data)
{
Chart c = new Chart();
((System.ComponentModel.ISupportInitialize)c).BeginInit();
c.Series.Clear();
c.BackColor = Color.White;
c.Height = 300;
c.Width = 500;
c.Palette = ChartColorPalette.Bright;
Series s = new Series(data.Key);
s.ChartType = SeriesChartType.Spline;
double maxValue = 0;
//NOTE: Going logarithmic on this, too big numbers
for (int i = 0; i < data.Value.Count; i++)
{
maxValue = Math.Max(Math.Log10(data.Value[i]), maxValue);
}
for (int i = 0; i < data.Value.Count; i++)
{
s.Points.AddXY(i,Math.Log10(data.Value[i]) * c.Height / maxValue);
}
c.Series.Add(s);
return c;
}
Many thanks in advance.
When you create a Chart yourself, in code, it does not contain any ChartArea.
Therefore, nothing is displayed.
I'm guessing that the designer generates some code to initialize a default chart area when you drag and drop a chart control onto the form.
Also, you should really let the chart control handle the layout, instead of calculating the desired position of each point based on the height of the chart control.
I would go as simple as possible to get something that's working, and then you can tweak the range of the axis afterwards. You can also set an axis to be logarithmic.
Start with trying out this minimal version, and make sure that displays something, before you complicate things. This works for me.
private Chart getChartForData(string key, List<double> data)
{
Chart c = new Chart();
Series s = new Series(key);
s.ChartType = SeriesChartType.Spline;
for (int i = 0; i < data.Count; i++)
{
s.Points.AddXY(i, data[i]);
}
c.Series.Add(s);
var area = c.ChartAreas.Add(c.ChartAreas.NextUniqueName());
s.ChartArea = area.Name;
// Here you can tweak the axis of the chart area - min and max value,
// where they display "ticks", and so on.
return c;
}
Related
I'd like to display coverarts for each album of an MP3 library, a bit like Itunes does (at a later stage, i'd like to click one any of these coverarts to display the list of songs).
I have a form with a panel panel1 and here is the loop i'm using :
int i = 0;
int perCol = 4;
int disBetWeen = 15;
int width = 250;
int height = 250;
foreach(var alb in mp2)
{
myPicBox.Add(new PictureBox());
myPicBox[i].SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage;
myPicBox[i].Location = new System.Drawing.Point(disBetWeen + (disBetWeen * (i % perCol) +(width * (i % perCol))),
disBetWeen + (disBetWeen * (i / perCol))+ (height * (i / perCol)));
myPicBox[i].Name = "pictureBox" + i;
myPicBox[i].Size = new System.Drawing.Size(width, height);
myPicBox[i].ImageLocation = #"C:/Users/Utilisateur/Music/label.jpg";
panel1.Controls.Add(myPicBox[i]);
i++;
}
I'm using the same picture per picturebox for convenience, but i'll use the coverart embedded in each mp3 file eventually.
It's working fine with an abstract of the library (around 50), but i have several thousands of albums. I tried and as expected, it takes a long time to load and i cannot really scroll afterward.
Is there any way to load only what's displayed ? and then how to assess what is displayed with the scrollbars.
Thanks
Winforms really isn't suited to this sort of thing... Using standard controls, you'd probably need to either provision all the image boxes up front and load images in as they become visible, or manage some overflow placeholder for the appropriate length so the scrollbars work.
Assuming Winforms is your only option, I'd suggest you look into creating a custom control with a scroll bar and manually driving the OnPaint event.
That would allow you to keep a cache of images in memory to draw the current view [and a few either side], while giving you total control over when they're loaded/unloaded [well, as "total" as you can get in a managed language - you may still need tune garbage collection]
To get into some details....
Create a new control
namespace SO61574511 {
// Let's inherit from Panel so we can take advantage of scrolling for free
public class ImageScroller : Panel {
// Some numbers to allow us to calculate layout
private const int BitmapWidth = 100;
private const int BitmapSpacing = 10;
// imageCache will keep the images in memory. Ideally we should unload images we're not using, but that's a problem for the reader
private Bitmap[] imageCache;
public ImageScroller() {
//How many images to put in the cache? If you don't know up-front, use a list instead of an array
imageCache = new Bitmap[100];
//Take advantage of Winforms scrolling
this.AutoScroll = true;
this.AutoScrollMinSize = new Size((BitmapWidth + BitmapSpacing) * imageCache.Length, this.Height);
}
protected override void OnPaint(PaintEventArgs e) {
// Let Winforms paint its bits (like the scroll bar)
base.OnPaint(e);
// Translate whatever _we_ paint by the position of the scrollbar
e.Graphics.TranslateTransform(this.AutoScrollPosition.X,
this.AutoScrollPosition.Y);
// Use this to decide which images are out of sight and can be unloaded
var current_scroll_position = this.HorizontalScroll.Value;
// Loop through the images you want to show (probably not all of them, just those close to the view area)
for (int i = 0; i < imageCache.Length; i++) {
e.Graphics.DrawImage(GetImage(i), new PointF(i * (BitmapSpacing + BitmapWidth), 0));
}
}
//You won't need a random, just for my demo colours below
private Random rnd = new Random();
private Bitmap GetImage(int id) {
// This method is responsible for getting an image.
// If it's already in the cache, use it, otherwise load it
if (imageCache[id] == null) {
//Do something here to load an image into the cache
imageCache[id] = new Bitmap(100, 100);
// For demo purposes, I'll flood fill a random colour
using (var gfx = Graphics.FromImage(imageCache[id])) {
gfx.Clear(Color.FromArgb(255, rnd.Next(0, 255), rnd.Next(0, 255), rnd.Next(0, 255)));
}
}
return imageCache[id];
}
}
}
And Load it into your form, docking to fill the screen....
public Form1() {
InitializeComponent();
this.Controls.Add(new ImageScroller {
Dock = DockStyle.Fill
});
}
You can see it in action here: https://www.youtube.com/watch?v=ftr3v6pLnqA (excuse the mouse trails, I captured area outside the window)
I'm trying to realize a chess game in C# with WPF. By now I visualized the chess grid and the figures in it. My .xaml file contains just a grid (named "playground") which is 8x8. The initializing in the .xaml.cs file looks like the following:
for (int x = 0; x < 8; x++)
{
for (int y = 0; y < 8; y++)
{
Border border = new Border();
if (y % 2 == (x % 2 == 0 ? 0 : 1))
{ // Chess like look
border.Background = black; //black is a static SolidColorBrush
}
// a ChessTile is an Image which can be a Figure or an EmptyTile
// omitted code... evaluate whether what figure the tile is or empty
ChessTile tile;
Grid.SetColumn(border, x);
Grid.SetRow(border, y);
border.HorizontalAlignment = HorizontalAlignment.Stretch;
border.VerticalAlignment = VerticalAlignment.Stretch;
if (tile is Figure)
{
// Set Event to border so the user is able to click outside the image
border.MouseDown += ClickFigure;
}
border.Child = tile; // Set tile as Child
playground.Children.Add(border); // Add border to Child
}
}
After initializing I want to move a chess figure to another Column and or Row in the grid. Currently I am evaluating in ClickFigure on which empty tile the figure is able to move to, then adding a new Handler to these empty tiles and moving my Figures there. I'm calling tile.Move(otherTile) in the Handler:
(from ChessTile.cs)
public Vector Coordinates
{
get
{
return position; //private Vector
}
private set
{
position = value;
Grid.SetRow(this, (int)position.X); //relocate this (ChessTile)
Grid.SetColumn(this, (int)position.Y);
}
}
public void Move(ChessTile other)
{
Vector temp = position;
Coordinates = other.position; //relocating both tiles through properties
other.Coordinates = temp;
}
The problem now is that the grid does not do anything at all. I searched for some method to update/repaint the grid manually but I didn't find anything. I also tried to remove the child first and then to add it again but that gave me an error saying that the child was already bound to an UIElement and that I would have to seperate it first.
Do you have any idea what I am doing wrong?
You need to change the row and column on the original Border placed inside the Grid.
In your setup code, you set column and row on a border object:
Grid.SetColumn(border, x);
Grid.SetRow(border, y);
But then in your Coordinates property, you're setting the column and row on this.
Grid.SetRow(this, (int)position.X); //relocate this (ChessTile)
Grid.SetColumn(this, (int)position.Y);
Assuming this is a custom class you've written, then you need to make sure it has a reference to the original Border and change your Coordinate setter to:
Grid.SetRow(border, (int)position.X); //relocate the border!
Grid.SetColumn(border, (int)position.Y);
Notice the change from this => border.
I'm familiar with C# and Winform, but new to Chart control. I'm using VS2013 built in System.Windows.Forms.DataVisualization.Charting.Chart to display bar for two points. Here's simply the code:
private void Form1_Load(object sender, EventArgs e)
{
Chart c = new Chart();
c.Dock = DockStyle.Fill;
ChartArea a = new ChartArea();
a.AxisX.Minimum = 0;
a.AxisX.Maximum = 4;
a.AxisY.Minimum = 0;
a.AxisY.Maximum = 2;
c.ChartAreas.Add(a);
Series s = new Series();
//*******************
s.Points.AddXY(1, 1);
//s.Points.AddXY(2, 2);
s.Points.AddXY(3, 2);
//*******************
c.Series.Add(s);
this.Controls.Add(c);
}
Please note the commented part, the points (2,2) and (3,2) are only different data, and have nothing to do with display style(I guess?). So this behavior seems very strange and so far I haven't found any solution to keep displaying (3,2) like (2,2).
You need to add the below codes in order to achieve the desired output:
1] Fix the interval for the Axis so the axis label and Grid lines do not change.
a.AxisX.Interval = 1;
2] Fix the bar width for the series. You can use PixelPointWidth to specify the width in Pixels or PointWidth in Points.
Example using PixelPointWidth:
s["PixelPointWidth"] = "20";
Also, since you are using c.Dock = DockStyle.Fill; to fill the chart into whole form, fixed width will not be good if you scale the form.
You can use MinPixelPointWidth and MaxPixelPointWidth to give the range to the width.
s["MinPixelPointWidth"] = "20";
s["MaxPixelPointWidth"] = "80";
Check this link for details on different chart elements.
Technical Reference for detailed documentation on Chart controls. This may be long but important to understand the details.
I have a GUI with a button that will open a new window. As soon as the window opens I need it to be filled with a sires of pictures that I have stored in a dictionary of (string,bitmap) with the string representing the path name. Obviously I need to iterate across the dictionary but I don't know what code to use to display the pictures. Is there anyway to make a loop that will automatically display the images in a set size.
For a good example of the output I'm looking for, think windows explorer thumbnails when browsing a folder of pictures.
I know very little about working with images or GUIs so any assistance would be appreciated.
As of now my this is the code I have:
MyPalletGui.Show();
PictureBox myPicBox = new PictureBox();
Dictionary<string,Bitmap> MyPallet = MyImageCollection.ToDictionary();
int xcor = 0;
int ycor = 0;
foreach (Bitmap curtImage in MyPallet.Values){
xcor += 50;
ycor += 50;
myPicBox.Location = new Point(xcor, ycor);
myPicBox.Width = 50;
myPicBox.Height = 50;
myPicBox.Visible = true;
myPicBox.Image = new Bitmap(curtImage);
this.MyPalletGui.Controls.Add(myPicBox);
I need to work with the x and y coordinates more, but the code above displays nothing.
I've made a dynamic 3d piechart using devexpress. I'm really impressed with how good the control feature is. I've hit a little bit of a downer though.
I would like my pie chart points to have different colors that I set in code (this will later be changed by the user using some form of pallet or combo box, not sure yet). Unfortunatly I can't seem to get a color method for the points of my data series.
Here's the code excluding the mass of commented out attempts:
Series series1 = new Series("Series1", ViewType.Pie3D);
chartControl2.Series.Add(series1);
series1.DataSource = chartTable;
series1.ArgumentScaleType = ScaleType.Qualitative;
series1.ArgumentDataMember = "names";
series1.ValueScaleType = ScaleType.Numerical;
series1.ValueDataMembers.AddRange(new string[] { "Value" });
//series1.Label.PointOptions.PointView = PointView.ArgumentAndValues;
series1.LegendPointOptions.PointView = PointView.ArgumentAndValues;
series1.LegendPointOptions.ValueNumericOptions.Format = NumericFormat.Percent;
series1.LegendPointOptions.ValueNumericOptions.Precision = 0;
// Adjust the value numeric options of the series.
series1.Label.PointOptions.ValueNumericOptions.Format = NumericFormat.Percent;
series1.Label.PointOptions.ValueNumericOptions.Precision = 0;
// Adjust the view-type-specific options of the series.
((Pie3DSeriesView)series1.View).Depth = 20;
((Pie3DSeriesView)series1.View).ExplodedPoints.Add(series1.Points[0]);
((Pie3DSeriesView)series1.View).ExplodedPoints.Add(series1.Points[1]);
((Pie3DSeriesView)series1.View).ExplodedPoints.Add(series1.Points[2]);
((Pie3DSeriesView)series1.View).ExplodedPoints.Add(series1.Points[3]);
((Pie3DSeriesView)series1.View).ExplodedDistancePercentage = 20;
chartControl2.Legend.Visible = true;
So I need something like chartcontrol2.series1.point[0].color = color.blue; something like this.
Drawing in charts when drawing the series points of charts. To do this you should handle the ChartControl.CustomDrawSeriesPoint event, and then you can change some of the drawing parameters using its event args.
check these events to do your functinality..
How to: Custom Paint Series Points
ChartControl.CustomDrawSeries Event
You need to define a chart Palette or use an existing one defined by DevExpress. See this
http://documentation.devexpress.com/#XtraCharts/CustomDocument7434