Teechart's DrawValue - c#

If I have a candle series drawn on the teechart, and I change the last candle's CloseValue, the candle does not automatically update on the chart. I don't really want to invalidate the whole chart to show it because if there are a lot of candles, that's slow. I thought the DrawValue method of the Candle series would solve this, but it seems that it actually redraws the whole chart as well. Is there a way to update/redraw on screen just that one candle?
I know when I was doing this in Delphi 5 the candle seemed to update without redrawing the whole chart - although maybe the whole chart updates were fast enough that it just seemed that way. In general, it seems that C#'s Teechart draws are 3 to 5 times slower than the Delphi 5 VCL Teechart's draws...

I think you can use the method that allow refresh only the series, RefreshSeries, as do in next line of code:
candle1.RefreshSeries();
Could you tell me, if it works for you? If you have any problems please let me know.
Update information to answer next question:
Ok, I guess I am not explaining myself well here, Sandra. Let me try
again. Let's say I have a chart that has only one series - 30,000
candles. Let's say I show ALL candles on the chart. Redrawing all 30K
candles takes time. Let's say the code changes only ONE candle - the
last one. Is there any way to just repaint that little portion of the
chart that has the new candle, and not repaint the whole chart in
order to show the new change? I tried Invalidate function passing it
the candle's rectangle, but it seems that Invalidate() and
Invalidate(rect) produce exactly same results.
Can you tell us which version of TeeChart are you using?. In the other hand, I have made a simple code where I have modified the CloseValue and I have released that isn't necessary repaint, redraw or refresh Series because the value is updated automatically. I have made the test using last version of TeeChartFor.Net Build number [4.1.2012.01312] and next code:
Steema.TeeChart.Styles.Candle candleSeries1;
Random r;
double tmpOpen;
double tmpClose;
int count;
DateTime dt;
TimeSpan ts;
private void InitializeChart()
{
tChart1.Aspect.View3D=false;
tChart1.AutoRepaint = false;
r = new Random();
candleSeries1.Clear();
candleSeries1.XValues.DateTime = true;
candleSeries1.GetHorizAxis.Labels.Angle = 90;
count = 0;
dt = DateTime.Today;
ts = TimeSpan.FromDays(1);
candleSeries1.Pen.Visible = false;
for (int t=0;t<30000;t++)
{
tmpOpen = r.Next(100);
tmpClose = tmpOpen - r.Next(100);
++count;
candleSeries1.Add(dt,tmpOpen,tmpOpen + r.Next(50),
tmpClose -r.Next(50),tmpClose);
dt += ts;
}
tChart1.AutoRepaint = true;
}
private void button1_Click(object sender, EventArgs e)
{
tmpOpen = r.Next(100);
tmpClose = tmpOpen - r.Next(100);
candleSeries1[candleSeries1.LastVisibleIndex].Close = tmpOpen;
}
Thanks,

Related

C# WinForm chart data wont display

So I'm having a really weird problem in C# WindForm. I've tried to create a chart. In the area where the chart itself should exist, the problem is that it displays no data. It just looks like that:
That's my code:
int questionsAmount = setData.questionsAmount;
var dates = new List<DateTime>();
foreach(var item in setData.dates)
{
dates.Add(Convert.ToDateTime(item));
}
int[] records = setData["records"].ToObject<int[]>();
Series series = new Series("records");
series.Points.DataBindXY(dates, records);
series.ChartType = SeriesChartType.Spline;
chart = new Chart();
chart.Name = set.title;
chart.Series.Add(series);
chart.Series["records"].SetDefault(true);
chart.Series["records"].Enabled = true;
chart.BackColor = SystemColors.Highlight;
Controls.Add(chart);
Refresh();
Any help would be much appreciated. Thanks!
Hmm.. First, because we cannot read the entire code, I cannot sure the data generating process is working well. As your picture, the chart control is added. Maybe you can add breakpoint on "series.Points.DataBindXY(dates, records);" and check whether the data is ready or not.
FYI, another probable issue is you need to do UI operation in main thread (UI thread) if you didn't.
Hope these can help you.

DataGridView flickers ONLY when the rows fits roughly one page

I have a grid view that is as basic as it can gets. It displays a one-off search result, and does not updates its datasource unless the search button is click again, so I don't think data update is causing the issue.
For example, my search returns 30 rows of results, and my full-screen data grid could fit 40 rows without scrolling, there are no flickering. Then I started to slowly reduce the height of the window. Once the grid view height is slightly less than my 30 rows' height it starts to flicker. But if I further decrease the height of the window so that the grid view could only shows 20 rows, a scrollbar shows up as expected and everything is fine again. Even scrolling up or down doesn't flicker at all.
Although I don't think it is performance related, I've tried things like suspend layout and double buffering which doesn't help as expected.
I think it is something related to auto row height calculation when it is near its boundary condition, but I have no idea how to solve it. Below are the auto row size settings that might/might not affect it.
dgv.DataSource = datatable;
dgv.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.AllCells;
dgv.Sort(dgv.Columns["Creation Date"], ListSortDirection.Ascending);
dgv.Columns["Creation Date"].SortMode = DataGridViewColumnSortMode.Automatic;
dgv.Columns["Name"].SortMode = DataGridViewColumnSortMode.Automatic;
dgv.Columns["Name"].AutoSizeMode = DataGridViewAutoSizeColumnMode.AllCells;
dgv.Columns["Name"].DefaultCellStyle.WrapMode = DataGridViewTriState.True;
dgv.Columns["Receipt No."].SortMode = DataGridViewColumnSortMode.Automatic;
dgv.Columns["Receipt No."].AutoSizeMode = DataGridViewAutoSizeColumnMode.AllCells;
dgv.Columns["Receipt No."].DefaultCellStyle.WrapMode = DataGridViewTriState.True;
dgv.Columns["Remark"].DefaultCellStyle.WrapMode = DataGridViewTriState.True;
dgv.Columns["Remark"].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
This is my double buffering class just in case I messed up
public static class BufferedGridView
{
public static void DoubleBuffered(this DataGridView dgv, bool setting)
{
Type dgvType = dgv.GetType();
PropertyInfo pi = dgvType.GetProperty("DoubleBuffered", BindingFlags.Instance | BindingFlags.NonPublic);
pi.SetValue(dgv, setting, null);
}
}
This is where I used it, in the Form that contains the grid view;
public ManagementPage()
{
InitializeComponent();
dgv.DoubleBuffered(true);
.......
}
In response to the comment below, I've set out the answer again below. Sorry, I'm fairly new to this site - just trying to help.
It seems you can break the endless loop that causes the flicker by overriding the OnCellValueChanged and OnRowHeightChanged events:
protected override void OnCellValueChanged(DataGridViewCellEventArgs e)
{
if (e.RowIndex != -1)
{
this.Rows[e.RowIndex].MinimumHeight = 2;
}
base.OnCellValueChanged(e);
}
and
protected override void OnRowHeightChanged(DataGridViewRowEventArgs e)
{
e.Row.MinimumHeight = e.Row.Height;
base.OnRowHeightChanged(e);
}
This basically locks the minimum row height to the height when the row height is updated, and then releases it when the cell value is changed.
Therefore, when the internal DataGridView code tries to resize the rows, they get locked to a minimum (meaning they won't be reduced in size again, which is what causes the endless loop). When a user (or code) updates a cell, this constraint is released so that the rows can resize when they need to.
I had this problem and after some analysis concluded it's a bug in .NET that happens in the edge case of when a DataGridView is roughly the same height as the rows it needs to contain, because of an endless loop of adding a vertical scrollbar, adjusting row heights, then removing the scrollbar etc.
I've posted the solution I came up with here: DataGridView - apparent bug in .NET causing continuous jitter on the grid view when number of rows is roughly the same as the size of the view

updating datagridview from within a thread causes error c# winform

I am writing a C# winform that uses a datagridview object. At a certain point in the program I need to update said datagridview from within a thread. This causes the program to throw an unhandled exception saying that object reference not set to an instance of an object, and makes the datagridview turn into the error image of a white box with an red X in it.
Specifically what I am trying to do in this case is update values in the rows, by removing the rows and updating the statistical values and reinserting the rows.
I do this in a separate function that the thread calls and it updates two rows in the datagridview every time.
Here is the code for my update function:
private void updateStats(int binNumber)
{
binNumber *= 2;//has to multiply by 2 because every bin has two rows in this table
if (statsData.Rows.Count >= (binNumber + 1))
{
statsData.Rows.RemoveAt(binNumber);
//number does not change because the index was just decreased by one
statsData.Rows.RemoveAt(binNumber);//because every bin requires two rows
}
Bin bin = bins[binNumber / 2];
List<double> realETempData = new List<double>();
List<double> imagETempData = new List<double>();
List<double> realAlphaTempData = new List<double>();
List<double> imagAlphaTempData = new List<double>();
//updates average and std dev values
foreach (MinSearchData dataPoint in bin.binData)
{
realETempData.Add(dataPoint.RealE);
imagETempData.Add(dataPoint.ImagE);
realAlphaTempData.Add(dataPoint.RealAlpha);
imagAlphaTempData.Add(dataPoint.ImagAlpha);
}
bin.AverageRealE = realETempData.Average();
bin.AverageImagE = imagETempData.Average();
bin.StdDevRealE = calculateStandardDeviation(realETempData);
bin.StdDevImagE = calculateStandardDeviation(imagETempData);
bin.AverageRealAlpha = realAlphaTempData.Average();
bin.AverageImagAlpha = imagAlphaTempData.Average();
bin.StdDevRealAlpha = calculateStandardDeviation(realAlphaTempData);
bin.StdDevImagAlpha = calculateStandardDeviation(imagAlphaTempData);
realETempData.Clear();
imagETempData.Clear();
realAlphaTempData.Clear();
imagAlphaTempData.Clear();
DataRow myRow = statsData.NewRow();
myRow[0] = bin.BinName;
myRow[1] = "Real";
myRow[2] = bin.AverageRealAlpha;
myRow[3] = bin.StdDevRealAlpha;
myRow[4] = bin.AverageRealE;
myRow[5] = bin.StdDevRealE;
statsData.Rows.InsertAt(myRow, binNumber);
DataRow myRow2 = statsData.NewRow();
myRow2[0] = "";
myRow2[1] = "Imaginary";
myRow2[2] = bin.AverageImagAlpha;
myRow2[3] = bin.StdDevImagAlpha;
myRow2[4] = bin.AverageImagE;
myRow2[5] = bin.StdDevImagE;
statsData.Rows.InsertAt(myRow2, binNumber + 1);
}
The stranger part of this is that I seem to be unable to catch the exception, and it happens inconsistently, as in sometime it fails the first time it tries to update, and other times it fails on the first try.
Any help would be greatly appreciated,
Thanks,
-Jake
This may cause due to race problem. In other words, one thread (likely the main thread) tries to paint the grid based on its current list of rows while the other thread infers and manipulates it. Moreover, I guess you have turned off CheckForIllegalCrossThreadCalls to be able to manipulate the grid directly over threads. If it is, this is the main cause of the problem. Anyway, the possible solution is to use BeginInvoke to indirectly work with the control:
private void updateStats(int binNumber)
{
datagridview1.BeginInvoke(new MethodInvoker(() =>
{
binNumber *= 2;//has to multiply by 2 because every bin has two rows in this table
if (statsData.Rows.Count >= (binNumber + 1))
....
....
....
myRow2[5] = bin.StdDevImagE;
statsData.Rows.InsertAt(myRow2, binNumber + 1);
}
}
EDIT:
I thought statsData is a DataGridView. Based on OP's comment it is a DataTable. So, I tailored the answer to reflect this fact.

Can i put this into a class function? (counter)

Working with a WPF application.
I am very much wonderin if it is possible to get a function luke the examle below into a class function (im not yet very experianced with C#).
private void counter01_Tick(object sender, EventArgs e)
{
if (counter01Ticks > 0)
{
//subtract 1 each time
counter01Ticks--;
//subtrack 1 secon each time
counter01Span = counter01Span.Subtract(TimeSpan.FromSeconds(1));
//update the progressbar
progBar01.Value++;
//get the % to show
progBar01Text.Text = Convert.ToString(Math.Round(((progBar01.Value / progBar01.Maximum) * 100), 0)) + "%";
//Label1 will show the count down.
string countDown = counter01Span.ToString();
TimeRemain01.Content = countDown;
}
else
{
counter01.Stop();
resetCounter01();
WarningMessage msgWarnOne = new WarningMessage();
msgWarnOne.warnMessage.Text = Properties.Settings.Default.msgScout01;
msgWarnOne.ShowDialog();
}
}
It is just a part of a counter. but i want to add more counters to my application later on.
Therefore i marked all the parameters with a number (01) in my code.
So what i do not want to do, i copy-paste the code and change the number for every counter, but rather have the number as a input number or something.
Would that be possible?
if i9 would understand it for this small part of code, i think i will be able to do it with the other parts too (above is only the tick form a counter).
#Users that user the answer below:
http://www.c-sharpcorner.com/uploadfile/mahesh/user-control-in-wpf/
Has helped me understand it better and migh be usefull to read too.
Yes you can put all of this (together with your XAML declaration) into a WPF-user control and put multiple of these into other Windows/Controls/...
Just look at the tutorial I linked in - should explain everything you need.
You can use method Control.FindControl;
Another approach is to make user control - and therefore you will work with only 1 array of controls in every counter_Tick

datagridview virtual mode, update RowCount causes CellValueNeeded to fire for all rows

I'm trying to implement datagridview's virtual mode but when i set RowCount to some number (to show the scrollbar) the grid wants to have all rows at once, not only the displayed.
DataGridView grid = new ...;
grid.VirtualMode = true;
grid.CellValueNeeded += OnCellValueNeeded;
grid.RowCount = dataprovider.GetFullCount();
How can i tell the grid to only request the rows displayed?
This is just a guess, but do you have the AutoSizeRowsMode or AutoSizeColumnsMode values set to AllCells or are any of the columns set to that either? Try setting the resize mode to None or just DisplayedCells and see if there is still a problem.
Not sure if this is the same problem as I was having, but I did get very poor performance when regularly drastically changing the RowCount on a VirtualMode DataGridView.
What I noticed was that the scroll bar was changing "slowly"; i.e. it looked like it was individually removing my virtual rows (!).
Anyway, doing a grid.Rows.Clear() before each call to grid.RowCount = n drastically improved performance.
You should set the RowCount to zero before setting the full count.
DataGridView grid = new ...;
grid.VirtualMode = true;
grid.CellValueNeeded += OnCellValueNeeded;
grid.RowCount = 0;
grid.RowCount = dataprovider.GetFullCount();
unfortunatly this seems to be the standard behavior. i could solve it either by
void OnCellValueNeeded(...)
{
if(!_active) return;
}
grid.VirtualMode = true;
grid.CellValueNeeded += OnCellValueNeeded;
_active = false;
grid.RowCount = dataprovider.GetFullCount();
_active = true;
or implementing IBindingList, ITypedList with complex lazy fetching in background thread
Update: the problem seems to be fixed now. I can not reproduce it anymore using the following:
static class Program
{
private static Form form;
private static int i;
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
var grid = new DataGridView
{
Dock = DockStyle.Fill,
VirtualMode = true,
AllowUserToAddRows = false,
Columns =
{
new DataGridViewTextBoxColumn { HeaderText = "foo" },
new DataGridViewTextBoxColumn { HeaderText = "bar" },
},
};
grid.CellValueNeeded += OnCellValueNeeded;
form = new Form
{
Controls = { grid }
};
//grid.RowCount = 0;
grid.RowCount = 10000;
Application.Run(form);
}
private static void OnCellValueNeeded(object sender, DataGridViewCellValueEventArgs e)
{
i++;
form.Text = i.ToString();
e.Value = "fooValue";
}
}
I'm having the same issue and I tried the solution of setting an active flag like you did, and I also tried the solution of setting the RowCount to 0 (or grid.Rows.Clear()) before setting the new RowCount.
Both of these things improved performance, but neither sped it enough to be as instantaneous as I want because I am dynamically filtering the grid in real time based on input from a search box.
I found two other solutions:
1) Use pagination so that you don't have to set the RowCount so high to begin with. I think this is a great solution if you are already using pagination (in which case you wouldn't be here) but an overly cumbersome one if you weren't planning on implementing it.
2) Put the call to set the RowCount in its own thread. This is the one I'm about to attempt. Honestly not sure how safe this one is if you're trying to edit cells while the thread is still finishing up but I guess I'll find out soon enough.
EDIT:
OK, so I tried threading it hoping that would do the trick since I read elsewhere that it really helped another guy. It seems to be a good solution IF you are just going to change the value once in a while but it still hangs if you do it multiple times consecutively (which I am). I think this is because you have to use Invoke() and the second time around it's still waiting for the first one to finish. Can't say I totally understand what the deal is but I've decided just to live with the blank rows for now because it's SOOO much faster and less complex when I just leave them there.

Categories