I am new in Winform Application. I am trying to implement a line chart which have multiple series and have a checkedListbox to select the particular series.
Code:
if (tbROI.SelectedTab == tbROI.TabPages["tbPageROIPoint"])
{
//If all ROI TAB
myIndex = GetMyChartIndex(mSeries, chartPointROI); // 4 for Point ROI tab
m_PointDataCounter++;
if (m_PointDataCounter > 15)
{
if (myIndex > 5)
{
chartPointROI.Series[mSeries].Points.RemoveAt(0);
m_PointDataCounter--;
}
}
if (cbListPOI.GetItemChecked(ROIIndex))
{
chartPointROI.Series[mSeries].Points.AddXY(timestring, mData);
chartPointROI.ResetAutoValues();
}
}
Using this code I am putting the data on the chart control.
The X-axis representing Time and Y-axis representing Data.
Initailly when I select any item of listbox the series starts from left side, but after sometime if I start one more series it will also start from left side but I want to start that from the current time which is representing on X-axis.
And when I stop any series after some time if I again start the same series I want to some gap in the series so that it can be clearly seen that series is been stopped.
In my case the series always starts from left side. And if I stop any series and start that again It will continue where it stopped.
Thanks in advance
EDIT:
It is showing that
Here is an example of how to remove a few DataPoints and also of how to restore them.
Note the flat line in the gap. If you want to 'remove' that line best color the last point transparent; I have added commented code for this.
List<DataPoint> marked = new List<DataPoint>();
int markedStartIndex = -1;
private void button1_Click(object sender, EventArgs e)
{
// I create a testperiod to remove
DateTime dt0 = DateTime.Now.AddMonths(2);
DateTime dt1 = dt0.AddHours(123);
DateTime dt2 = dt0.AddHours(173);
// convert to doubles:
double startPeriod = dt1.ToOADate();
double endPeriod = dt2.ToOADate();
// short reference
Series s = chart1.Series[0];
// select the points in the period. pick your border conditions!
marked = s.Points.Cast<DataPoint>()
.Where(x => x.XValue > startPeriod && x.XValue < endPeriod)
.ToList();
if (marked.Count < 1) return;
// remember where we started to remove
markedStartIndex = s.Points.IndexOf(marked.First());
foreach (DataPoint dp in marked) s.Points.Remove(dp);
// Optionally 'hide' the gap line
//if (markedStartIndex > 0) s.Points[markedStartIndex].Color = Color.Transparent;
}
The code to bring them back inserts them at the right spot and then clears the points.:
private void button2_Click(object sender, EventArgs e)
{
Series s = chart1.Series[0];
// optionally re-color the gap-line
//if (markedStartIndex > 0) s.Points[markedStartIndex].Color = s.Color;
foreach (DataPoint dp in marked) s.Points.Insert(markedStartIndex++, dp);
marked.Clear();
}
Result with a transparent gap:
You could also color the gap in red and also store more than one set of points; for this you would have to store the starting points as well as make sure to manage multiple periods when you re-insert them!
As an alternative to actually removing the points you could also choose to simply color them transparent..
I have this method:
private void plotGraph(List<float> data)
{
GraphPane myPane = zedGraphControl1.GraphPane;
// Set the Titles
myPane.Title.Text = "Symulacja";
myPane.XAxis.Title.Text = "Czas";
myPane.YAxis.Title.Text = "Wartość sygnału";
myPane.XAxis.Scale.Max = 20;
myPane.YAxis.Scale.Max = 5;
myPane.YAxis.Scale.Min = -5;
PointPairList PairList = new PointPairList();
double x = 0;
for (int i = 0; i <= 1000; i++)
{
PairList.Add(x, data[i]);
x += 0.01;
}
LineItem ACurve = myPane.AddCurve("Team A", PairList, Color.Red, SymbolType.None);
zedGraphControl1.Refresh();
zedGraphControl1.AxisChange();
}
And the first time I call it everything is ok, the function plots the plot i want (the Y values of the points are from the list data). Now the second and every other time i call it, there is a new line drawn over the first one (the old one stays on the chart). I would like the old one to dissapear when a new one is plotted, what should I do to get this effect?
You have to remove the old curve first. If all you'll ever have is one curve, you would remove the curve with the label "Team A", then add the PairList to a new instance. However, if in the future you want multiple curves simultaneously, you'll need to pass in the label as a parameter to your method.
To simply remove a lone curve only each time, add the following code before the call to AddCurve. An index of -1 indicates the CurveList element does not exist.
int curveIndex = myPane.CurveList.IndexOfTag("Team A");
if (curveIndex != -1)
{
myPane.CurveList.RemoveAt(curveIndex);
}
You can remove/add any number of curves, but you have to have a tag to identify the curve that you wish to change. The tag variable will replace the "Team A" above.
I assign manually values to the labels of the x-axis. Every 150 steps there should be a value. The rest is String.Empty. This part works perfectly.
Chart plotter = plotter_ref.Clone();
XSeries xseries = plotter.XValues.AddXSeries();
xseries.Add("0");
for (int i = 2; i <= 600; i++)
{
if (i % 150 == 0)
{
xseries.Add(i.ToString());
}
else
{
xseries.Add(String.Empty);
}
}
Now I would like to have Tickmarks at those points where the values are. I tried to set the MajorTickMark property
plotter.XAxis.MajorTick = 150;
plotter.XAxis.MajorTickMark = TickMarkType.Cross;
// or this
//plotter.XAxis.MajorTickMark = TickMarkType.Inside;
But it has no effect:
What am I doing wrong?
If I take a closer look at the Tutorial in the sample chart section their example also does not show any x-axis ticks. Is it at all possible?
what if you did another trick? i.e. while printing the values there, you did like this:
xseries.Add( "+" + Environment.NewLine + i.ToString()) );
thus, it will insert like this:
--------------------------------------
+ +
0 150 300
and just positioned 12pixel above?
I use Microsoft.DataVisualization.Charting and want to get the value of the point when i click on it.
My problem: i want exactly that value i clicked, even if its only a value calculated by the Chart and between 2 points.
Example: 3 points: P(0;3), P(1;6), P(3;12)
When i click at x-Value 2 i want to get 9 as result if the line is linear.
Currently i do that:
HitTestResult[] hits = chart.HitTest(e.X, e.Y, false, ChartElementType.PlottingArea);
//DataInformation save the DateTime and Value for later use
DataInformation[] dinfo = new DataInformation[hits.Length];
foreach (ChartArea area in chart.ChartAreas)
{
area.CursorX.LineWidth = 0; //clear old lines
}
for (int i = 0; i < hits.Length; i++) //for all hits
{
if (hits[i].ChartElementType == ChartElementType.PlottingArea)
{
//val saves the x-value clicked in the ChartArea
double val = hits[i].ChartArea.AxisX.PixelPositionToValue(e.X);
DataPoint pt = chart.Series[hits[i].ChartArea.Name].Points.Last(elem => elem.XValue < val);
dinfo[i].caption = hits[i].ChartArea.Name;
dinfo[i].value = pt.YValues[0].ToString();
//hits[i].ChartArea.CursorX.Position = pt.XValue;
}
}
This show the right values for every existing data point but not that clicked point.
How can i get the exact value?
It seems, there is no way to get the exact value. I changed to OxyPlot. OxyPlot can show the data much faster and you can get the exact value for any point.
we use this two methods to adjust column length based on Column content and header resp.
ListView.AutoResizeColumns(ColumnHeaderAutoResizeStyle.ColumnContent);
ListView.AutoResizeColumns(ColumnHeaderAutoResizeStyle.HeaderSize);
But how to adjust based on both? i.e. adjust to the longest length for header and column content.
lvw.Columns[0].Width = -2
See remarks in MSDN for details:
http://msdn.microsoft.com/en-us/library/system.windows.forms.columnheader.width.aspx
Also note that MSDN says that 'To autosize to the width of the column heading, set the Width property to -2.', but actually it works for column heading AND column contents.
Here is a code to prove that:
lvw.Columns.Add(new String('x', 25)); // short header
lvw.Items.Add(new String('x', 100)); // long content
lvw.Columns[0].Width = -2;
// in result column width will be set to fit content
As answered here, calling both resizing options do the job :
myListView.AutoResizeColumns(ColumnHeaderAutoResizeStyle.ColumnContent);
myListView.AutoResizeColumns(ColumnHeaderAutoResizeStyle.HeaderSize);
This is what I use to adjust column width to both content and header:
public static void autoResizeColumns(ListView lv)
{
lv.AutoResizeColumns(ColumnHeaderAutoResizeStyle.ColumnContent);
ListView.ColumnHeaderCollection cc = lv.Columns;
for (int i = 0; i < cc.Count; i++)
{
int colWidth = TextRenderer.MeasureText(cc[i].Text, lv.Font).Width + 10;
if (colWidth > cc[i].Width)
{
cc[i].Width = colWidth;
}
}
}
Example use:
autoResizeColumns(listView1);
The method isn't that well tested, but at least it works in the context I'm using it in.
It's possible indeed to use MeasureText and then to calculate how much space is left and somehow distribute between all columns. But this is quick-and-dirty approach which I have quickly coded:
/// <summary>
/// Enables autoresizing for specific listview.
/// You can specify how much to scale in columnScaleNumbers array - length of that array
/// should match column count which you have.
/// </summary>
/// <param name="listView">control for which to enable auto-resize</param>
/// <param name="columnScaleNumbers">Percentage or numbers how much each column will be scaled.</param>
private void EnableAutoresize(ListView listView, params int[] columnScaleNumbers)
{
listView.View = View.Details;
for( int i = 0; i < columnScaleNumbers.Length; i++ )
{
if( i >= listView.Columns.Count )
break;
listView.Columns[i].Tag = columnScaleNumbers[i];
}
listView.SizeChanged += lvw_SizeChanged;
DoResize(listView);
}
void lvw_SizeChanged(object sender, EventArgs e)
{
ListView listView = sender as ListView;
DoResize(listView);
}
bool Resizing = false;
void DoResize( ListView listView )
{
// Don't allow overlapping of SizeChanged calls
if (!Resizing)
{
// Set the resizing flag
Resizing = true;
if (listView != null)
{
float totalColumnWidth = 0;
// Get the sum of all column tags
for (int i = 0; i < listView.Columns.Count; i++)
totalColumnWidth += Convert.ToInt32(listView.Columns[i].Tag);
// Calculate the percentage of space each column should
// occupy in reference to the other columns and then set the
// width of the column to that percentage of the visible space.
for (int i = 0; i < listView.Columns.Count; i++)
{
float colPercentage = (Convert.ToInt32(listView.Columns[i].Tag) / totalColumnWidth);
listView.Columns[i].Width = (int)(colPercentage * listView.ClientRectangle.Width);
}
}
}
// Clear the resizing flag
Resizing = false;
}
And depending how many columns you have - you specify each column "percentage" or simply number. For example for 3 columns - call looks like this:
EnableAutoresize(listView1, 6, 3, 1);
This will distribute column sizes as:
6 * 100% / (6 + 3 + 1) = 60% for first column,
30% for next and 10% for remaining.
This is somehow poor man quick implementation. :-)
In my case, I do this through the next steps (for two columns of data):
Creating a ColumnHeader object for each column.
Setting the size by AutoResize based on HeaderSize (on both columns)
Store that value in a Integer variable
Setting the size by AutoResize based on ColumnContent (on both columns)
Updating the value of each Integer variable through the Max criteria between the old value and the new value (for each column).
Setting the column width size for each ColumnHeader object.
In VB.NET:
'Create two header objects as ColumnHeader Class
Dim header1, header2 As ColumnHeader
'Construcción de los objetos header
header1 = New ColumnHeader
header1.Text = "ID"
header1.TextAlign = HorizontalAlignment.Right
header1.Width = 10
header2 = New ColumnHeader
header2.Text = "Combinaciones a Procesar"
header2.TextAlign = HorizontalAlignment.Left
header2.Width = 10
'Add two columns using your news headers objects
ListView.Columns.Add(header1)
ListView.Columns.Add(header2)
'Fill three rows of data, for each column
ListView.Items.Add(New ListViewItem({"A1", "B1"}))
ListView.Items.Add(New ListViewItem({"A2", "B2"}))
ListView.Items.Add(New ListViewItem({"A3", "B3"}))
'Change the size of each column
Dim headsz1, headsz2 As Integer
SelectionInTable.ListView.AutoResizeColumn(0, ColumnHeaderAutoResizeStyle.HeaderSize)
SelectionInTable.ListView.AutoResizeColumn(1, ColumnHeaderAutoResizeStyle.HeaderSize)
headsz1 = header1.Width
headsz2 = header2.Width
SelectionInTable.ListView.AutoResizeColumn(0, ColumnHeaderAutoResizeStyle.ColumnContent)
SelectionInTable.ListView.AutoResizeColumn(1, ColumnHeaderAutoResizeStyle.ColumnContent)
headsz1 = Math.Max(headsz1, header1.Width)
headsz2 = Math.Max(headsz2, header2.Width)
header1.Width = headsz1
header2.Width = headsz2
Here's a C# solution that can be used for any ListView. It assumes your column count and headers won't change for any given list view. Get rid of the listViewHeaderWidths dictionary if you want to recalculate header widths every time (if headers change, or number of columns changes).
private Dictionary<string, int[]> listViewHeaderWidths = new Dictionary<string, int[]>();
private void ResizeListViewColumns(ListView lv)
{
int[] headerWidths = listViewHeaderWidths.ContainsKey(lv.Name) ? listViewHeaderWidths[lv.Name] : null;
lv.BeginUpdate();
if (headerWidths == null)
{
lv.AutoResizeColumns(ColumnHeaderAutoResizeStyle.HeaderSize);
headerWidths = new int[lv.Columns.Count];
for (int i = 0; i < lv.Columns.Count; i++)
{
headerWidths[i] = lv.Columns[i].Width;
}
listViewHeaderWidths.Add(lv.Name, headerWidths);
}
lv.AutoResizeColumns(ColumnHeaderAutoResizeStyle.ColumnContent);
for(int j = 0; j < lv.Columns.Count; j++)
{
lv.Columns[j].Width = Math.Max(lv.Columns[j].Width, headerWidths[j]);
}
lv.EndUpdate();
}
Anton Kedrov answer is best one but in my case i have a listview with more than 50 columns and i update its data frequently in this case i notice listview's this.AutoResizeColumns performs much faster work so i m writing this solution also
First Method by setting with to -2
public void AutoUpdateColumnWidth(ListView lv)
{
for (int i = 0; i <= lv.Columns.Count - 1; i++) {
lv.Columns(i).Width = -2;
}
}
Second method i used (less flicker on multiple calls)
public void AutoUpdateColumnWidth(ListView lv)
{
ListViewItem nLstItem = new ListViewItem(lv.Columns(0).Text);
for (int i = 1; i <= lv.Columns.Count - 1; i++) {
nLstItem.SubItems.Add(lv.Columns(i).Text);
}
v.Items.Add(nLstItem);
lv.AutoResizeColumns(ColumnHeaderAutoResizeStyle.ColumnContent);
lv.Items.RemoveAt(nLstItem.Index);
}
This is simple (although it took me a while to figure out)...
We know that the width must be at least as great as the column headers, so that we see all of the header text. Beyond that, the width can expand larger to accommodate contents. Hence, we do the following:
Autosize the columns to header.
Iterate through the columns and set the minimum width property for each column to the current column width (which guarantees your columns will never get too small to see the header).
From now on, autosize columns by content.
It is not necessary to track widths separately and reset them as other posters suggest. Setting the minimum width for the column solves the issue until the header text is changed, in which case you set the minimum width to 0, autosize just the modified column, and then set the minimum width to the current width again.
EDIT: My apologies, I forgot that I was not using the standard listview, but instead the 3rd party product BetterListView (a free version is available). The standard listview columns don't appear to support minimum width. I do recommend BetterListView highly as a great alternative (much better feature set and performance).