I'm attempting to open a chart in another window form, however the classes used for the data in the chart is in the first form. My goal here is to have a chart be able to open many times in a modeless window.
in form1.cs I build my chart:
Chart chart = new Chart();
Series price = new Series("Price"); //create new series
chart.Series.Add(price);
chart.Series["Price"].ChartType = SeriesChartType.Candlestick;
chart.Series["Price"]["OpenCloseStyle"] = "Candlestick";
chart.Series["Price"]["ShowOpenClose"] = "Both";
chart.Series["Price"]["PriceUpColor"] = "Green"; //Price increase = green
chart.Series["Price"]["PriceDownColor"] = "red"; //price decrease = red
for (int i = 0; i < data.Count; i++)
{
chart.Series["Price"].Points.AddXY(data[i].getDate(), data[i].getHigh()); //Adds date and high value
chart.Series["Price"].Points[i].YValues[1] = System.Convert.ToDouble(data[i].getLow()); //Low value added to chart
chart.Series["Price"].Points[i].YValues[2] = System.Convert.ToDouble(data[i].getOpen()); //open value added to chart
chart.Series["Price"].Points[i].YValues[3] = System.Convert.ToDouble(data[i].getClose()); //close value added to chart
}
Form2.cs:
public void DisplayChart(Chart newChart)
{
chart1 = newChart;
chart1.Show();
}
It is best to make the Chart is a separate class and call or create it from whichever form you need.
This follows object-oriented principles so you can reuse code and also use it for multiple purposes/views.
Related
I am currently using WinForms. I have two Lists of Values (doubles) which I visualize with the NuGet package "LiveCharts". Now to my problem: The list of the values constantly changes as new values are added to both lists with a frequency of up 200 values / second. Right now I am doing this with Random Values. In a loop I add a new random value to each list and remove the value with the index 0. Now I want the chart to update everytime a new value is added to the list, but although I call the method which should do that everytime I change the values, the chart only updates after the loop ended.
This method is called by a button and should add 10 new values. This works, but the chart only updates after the for-loop is finished, although the UpdateChart() method is called everytime.
private void ReloadButton_Click(object sender, EventArgs e)
{
for (int i = 0; i < 10; i++)
{
valueList1.RemoveAt(0);
AddRandomValueToList1();
valueList2.RemoveAt(0);
AddRandomValueToList2();
UpdateChart();
}
}
This is the UpdateChart() method I use to update the chart Revenue is a class object and contains a value and a DateTime
private void UpdateChart()
{
series1 = new SeriesCollection();
// series.Clear();
double[] values1 = new double[valueList1.Count];
double[] values2 = new double[valueList2.Count];
int index = 0;
foreach(Revenue current in valueList1)
{
values1[index] = current.value;
index++;
}
index = 0;
foreach (Revenue current in valueList2)
{
values2[index] = current.value;
index++;
}
series1.Add(new LineSeries()
{
Title = "Values1",
Values = new ChartValues<double>(values1)
});
series1.Add(new LineSeries()
{
Title = "Values2",
Values = new ChartValues<double>(values2)
});
cartesianChart1.Series = series1;
}
I have already tried to add breaks (Thread.Sleep) hoping the chart would refresh, but that did not work. As well as the cartesianChart1.Refresh() method which I called in the for-loop. Also did I disable the animations, but still got not the result I was hoping achieve.
Now I do not know what I can do to force the chart to refresh everytime I change the values of the lists in order to create a realtime chart.
I am grateful for every help
Thanks in advance
i have buttons this buttons for categories i want when click in this buttons i want to get rows from database products table and get two data product name and price and set it those values into dynamic buttons in windows application notes i have set location properities for button such like flowLayoutPanel
i have this code
private void btnhotdrink_Click_1(object sender, EventArgs e)
{
//int StartPositition = 100;
//int EndPosition = 10;
DataTable dt = new DataTable();
dt =clsprdcat.GET_HOTDRINK_CATEGORY();
for(int i=0; i < dt.Rows.Count; i++)
{
for (int s=0; s < dt.Columns[4]; s++)
{
Button l = addbuttons(i,s);
flowLayoutPanel1.Controls.Add(l);
//EndPosition +=70;
}
}
}
Button addbuttons(int i)
{
Button l = new Button();
l.Name = "Name" + l.ToString();
l.Text = "label" + l.ToString();
l.ForeColor = Color.White;
l.BackColor = Color.Green;
l.Font = new Font("Serif", 24, FontStyle.Bold);
l.Width = 170;
l.Height = 80;
//l.Location = new Point(start, end);
l.TextAlign = ContentAlignment.MiddleCenter;
l.Margin = new Padding(5);
return l;
}
How Can i Do this
All Buttons are inherently dynamically created at runtime. The Windows Forms Designer works via partial classes: You work on one part. The designer on the other (designer.cs). And at compile time the Designers code is executed by calling "InitializeComponents()" in the Constructor. It can only do exactly the same things you can do.
If you need help with creating buttons yourself, you can always look into the Designer part. Just do take care not to change anything in that part. This System only works because the designer is exclusively writing in the file and compilation/loading of a Project is prone to seizing up if you did manual changes.
As you wrote it right now, this code will create dt.Rows.Count buttons at the same locaiton. Usually the Button creation code it needs access to the loop variable, to adapt the positions of each consequtive element.. Personally I prefer to leave positioning of Elements as much to automatics as possible. These designs simply adapt better to changes in resolution and window size.
Updated the code to give you a better idea of what's happening and to allow the data to load faster.
I have still not seen anything posted online that answers this question.
I have a MS Chart control on a C# application (Visual Studio 2010 Express, if that makes a difference). Data loads at a slow realtime rate (once per second). My intent is to display 6 minutes of data at a time, with the ability to scroll to another page of data if necessary. I want the data to fill the chart from the left hand side. As the data is added to the chart, the scroll bar and X axis legends also move over so that I can only see the latest data point. I have to scroll to the left to see earlier data -- and the the scroll bar jumps back when the new data point is added. I want the scroll bar to stay in one place (left edge) unless I move it. I have a 1 second timer on my form and new data is added with every time cycle.
I hope this is sufficient. Any help?
Code to Initialize the Chart control:
DateTime startTime = DateTime.Now;
DateTime endTime = startTime.AddMinutes(6);
DateTime maxTime = startTime.AddMinutes(24);
// Bind the chart to the list.
chartAssociateProductivity.DataSource = Globals.listKohlsPerformanceDataSource;
chartAssociateProductivity.ChartAreas["ChartArea1"].CursorX.AutoScroll = true; // enable autoscroll
chartAssociateProductivity.ChartAreas["ChartArea1"].CursorX.IsUserEnabled = false;
chartAssociateProductivity.ChartAreas["ChartArea1"].CursorX.IsUserSelectionEnabled = false;
chartAssociateProductivity.ChartAreas["ChartArea1"].AxisX.Minimum = startTime.ToOADate();
chartAssociateProductivity.ChartAreas["ChartArea1"].AxisX.Maximum = endTime.ToOADate();
chartAssociateProductivity.ChartAreas["ChartArea1"].AxisX.ScaleView.Zoomable = true;
chartAssociateProductivity.ChartAreas["ChartArea1"].AxisX.ScrollBar.IsPositionedInside = true;
chartAssociateProductivity.ChartAreas["ChartArea1"].AxisX.ScaleView.Zoom(startTime.ToOADate(), 6, DateTimeIntervalType.Minutes);
chartAssociateProductivity.ChartAreas["ChartArea1"].AxisX.ScaleView.Position = 0;// startTime.ToOADate();
chartAssociateProductivity.ChartAreas["ChartArea1"].AxisX.MinorTickMark.Enabled = true;
// disable zoom-reset button (only scrollbar's arrows are available)
chartAssociateProductivity.ChartAreas["ChartArea1"].AxisX.ScrollBar.ButtonStyle = ScrollBarButtonStyles.SmallScroll;
// set scrollbar small change to blockSize (e.g. 100)
chartAssociateProductivity.ChartAreas["ChartArea1"].AxisX.ScaleView.SmallScrollSize = 100;
Code to Add the Data (for the example, I will add constant data):
private void timer1_Tick(object sender, EventArgs e)
{
listPerformanceDataSource.Add(new PerformanceRecord(0, 248));
}
The data structure:
public class PerformanceRecord
{
int bagCount, goal;
public PerformanceRecord(int bagCount, int goal)
{
this.bagCount = bagCount;
this.goal = goal;
}
public int BagCount
{
get { return bagCount; }
set { bagCount = value; }
}
public int Goal
{
get { return goal; }
set { goal = value; }
}
}
// Create a list.
public static List<PerformanceRecord> listPerformanceDataSource = new List<PerformanceRecord>();
ChartArea chartArea=new ChartArea();
chartArea = chart1.ChartAreas[series.ChartArea];
int scrollBarVal=chartArea.AxisX.ScaleView.Position;
you have to make a chartArea instance and use its axis position
Problem
i've run into an issue where deserializing any value with json.net (even dummy values) seems to mess up the dates along the x-axis of my mschart control. this is a chart that's supposed to have date values along the x-axis:
===bad===
the chart is produced by the following minimal code:
using System.Windows.Forms.DataVisualization.Charting;
using System.Threading;
using Newtonsoft.Json;
public partial class Form1 : Form
{
Thread thread;
public Form1()
{
InitializeComponent();
chart1.Dock = DockStyle.Fill;
thread = new Thread(Plot);
thread.Start();
}
void Plot()
{
// prepare chart
chart1.Invoke((MethodInvoker)delegate
{
chart1.ChartAreas.Clear();
chart1.Series.Clear();
ChartArea area = new ChartArea();
chart1.ChartAreas.Add(area);
Series series = new Series("water level");
series.ChartArea = area.Name;
series.ChartType = SeriesChartType.Line;
chart1.Series.Add(series);
});
// plot line by adding 2 points: ((time)0, 0) and ((time)1, 1)
for (int i = 0; i < 2; i++)
{
object dummy = JsonConvert.DeserializeObject<object>("null");
chart1.Invoke((MethodInvoker)delegate
{
DateTime time = new DateTime(1970, 1, 1, 0, 0, i, DateTimeKind.Utc);
chart1.Series["water level"].Points.AddXY(time, i);
});
}
}
}
commenting out the only json.net line following line results in the expected result:
object dummy = JsonConvert.DeserializeObject<object>("null");
===good===
Question
is there something wrong with my code?
Update
i just replaced the concerned line with
Thread.Sleep(5000);
and the problem still occurs. i thought that maybe i'm modifying the chart too early. but now, i'm not sure what to make of it.
i had a look through the properties of the Series object, and saw that XValueType was set to "Double". so i tried explicitly setting it to "DateTime" when creating it, and that solved the problem:
Series series = new Series("water level");
series.ChartArea = area.Name;
series.XValueType = ChartValueType.DateTime; /* <---- added this line */
series.ChartType = SeriesChartType.Line;
chart1.Series.Add(series);
it is set to "Auto" by default, which appareantly does not always get set to "DateTime". i guess i should have looked at the properties much earlier.
it's still a strange issue to me, and i have no idea what causes it, so this is more of a workaround.
I am using the Chart class in Visual Studio 2013 to visualize some of my data. However, my data quickly spawns many series and it's very important to have them all in one chart. I limited the legend area to 20% of the complete chart area, and so I pretty much cannot display more than 7-8 legend items when I stretch my chart to its maximum size. The control just puts ... after it runs out of space for legend items.
Instead of it just writing ..., is it somehow possible to add a scrollbar to the legend and be able to see all of the items? I am aware that I can implement my own legend in some way, but I would like to squeeze the most out of what the Chart class has to offer. I would also like to add checkboxes next to each legend item which would indicate whether the series should be hidden on the chart or not. Is this possible to do without my own legend implementation?
Additionally, I would also like to have a menu expand on right click on a legend item with a few options, but that's completely optional. Scrollbar and checkboxes are my main problem now.
Thanks.
General idea: You have to create two charts. One is main and second for legend only. You will have same series style if series order will be the same.
For showing pop up on right click on legend item:
Connect ContextMenu (ContextMenuStrip class in toolbox) to your legend's chart.
For showing hiding series from legend:
You have to implement MouseClick event handler and check what object is under mouse cursor using math (GetChildAtPoint() method doesn't work for legend items). Equation: is series_index = control_relative_mouse_y / c_legendItemHeight where c_legendItemHeight is value you provide to compute controls height (height of single legend item).
You have to configure your legend chart to contain LegendStyle to Row, MaximumAutoSize to 100, Docking to Left, IsTextAutoFit to false and IsEquallySpacedItems to true.
You have define 3 columns in your legend (one for series style, second for checkbox and third for series name). Use series CustomProperties to keep visibility state. In check column use this custom property (Text = "#CUSTOMPROPERTY(...)") to show check state. Chart does not support auto sizing. You can do it manually. During series load set your chart height to calculated value. This value equals to _stock.Shares.Count * c_legendItemHeight + 9. Where: _stock.Shares.Count is number of items in legend, c_legendItemHeight constant height of item (integer value, numbers grater then 18 seems to work for me), 9 (seems to be constant). I know it is not nice but I cannot find any better solution. I've added 502 series in my example and it worked fine. Make sure that you don't have any margin in your chart because otherwise you will be not able to calculate series number correctly.
For "many series in legend" problem:
Put your legend chart into a panel with AutoScroll property turned on. Set panels and legends height using expression from above description.
Source code:
public partial class Form1 : Form
{
private const int c_legendItemHeight = 20;
private const string c_checkCustomPropertyName = "CHECK";
private const string c_checkedString = "✔"; // see http://www.edlazorvfx.com/ysu/html/ascii.html for more
private const string c_uncheckedString = "✘";
private Stock _stock;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
_stock = Stock.Load();
// mainChart
mainChart.Legends.Clear();
foreach (Share share in _stock.Shares)
{
Series series = mainChart.Series.Add(share.Name);
series.ChartType = SeriesChartType.Line;
foreach (ShareQuotation shareQuotation in share.Quotations)
{
series.Points.AddXY(shareQuotation.Date.ToString(), shareQuotation.Close);
}
}
// LegendChart
Legend legend = legendChart.Legends[0];
legendChart.Series.Clear();
legend.IsTextAutoFit = false;
legend.IsEquallySpacedItems = true;
legend.MaximumAutoSize = 100;
legend.Docking = Docking.Left;
legend.LegendStyle = LegendStyle.Column;
legend.Position.Auto = true;
legend.Position.Width = 100;
legend.Position.Height = 100;
legend.CellColumns[1].Text = "#CUSTOMPROPERTY(" +c_checkCustomPropertyName+ ")";
foreach (Share share in _stock.Shares)
{
Series series = legendChart.Series.Add(share.Name);
series.SetCustomProperty(c_checkCustomPropertyName,c_checkedString);
}
legendChart.Height = _stock.Shares.Count * c_legendItemHeight + 9; // 9 - seems to be constant value
legendPanel.Height = legendChart.Height;
}
private void legendChart_MouseClick(object sender, MouseEventArgs e)
{
Point mousePosition = legendChart.PointToClient(Control.MousePosition);
int seriesNo = mousePosition.Y / c_legendItemHeight;
Series series = legendChart.Series[seriesNo]; // TODO - check if not out of range
if (e.Button == System.Windows.Forms.MouseButtons.Left)
{
// check uncheck series
if (series.GetCustomProperty(c_checkCustomPropertyName) == c_checkedString)
{
// if checked
// uncheck
series.SetCustomProperty(c_checkCustomPropertyName, c_uncheckedString);
series.CustomProperties = series.CustomProperties; // workaround - trigger change - is this a bug?
// hide in mainChart
mainChart.Series[seriesNo].Enabled = false;
}
else
{
// if unchecked
legendChart.Series[seriesNo].SetCustomProperty(c_checkCustomPropertyName, c_checkedString);
series.CustomProperties = series.CustomProperties; // workaround - trigger change - is this a bug?
// show in mainChart
mainChart.Series[seriesNo].Enabled = true;
}
}
}
private void contextMenu_Opening(object sender, CancelEventArgs e)
{
Point mousePosition = legendChart.PointToClient(Control.MousePosition);
int seriesNo = mousePosition.Y / c_legendItemHeight;
Series series = legendChart.Series[seriesNo]; // TODO - check if not out of range
contextMenu.Items.Clear();
string state = series.GetCustomProperty(c_checkCustomPropertyName) == c_checkedString ? "visible" : "hidden";
contextMenu.Items.Add("&Some strange action for " + state + " item named " + series.Name);
contextMenu.Items.Add("&Another action ...");
}
}
Result should look like this: