Home Forums WinForms controls Xceed Chart for WinForms Crash after calling Refresh() method

Viewing 13 posts - 1 through 13 (of 13 total)
  • Author
    Posts
  • User (Old forums)
    Member
    Post count: 23064
    #18843 |

    I have an application that gathers and plot data on a worker thread. Inside this thread I collect data every 1 second and call
    (LineSeries)m_chart.Series[0].Values.AddRange(dataArray) and (LineSeries)m_chart.Series[0].XValues.AddRange(timeArray)
    then call Refresh().

    Every 30 second I get an event that clears the chart by calling ((LineSeries)m_chart.Series[0]).Values.Clear() and ((LineSeries)m_chart.Series[0]).XValues.Clear()
    and then 1 second later it calls AddRange again and refresh again.

    I figured the crash happens after it calls the Refresh() then when an event to clear the graph happens before the callback to Refresh occurs? Is there a way to do this with crashing?
    Here’s the callstack:
    ************** Exception Text **************
    System.ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection.
    Parameter name: index
    at System.Collections.ArrayList.get_Item(Int32 index)
    at Xceed.Chart.Core.DataSeries.get_Item(Int32 index)
    at Xceed.Chart.Core.DataSeries.GetValueForIndex(Int32 index)
    at Xceed.Chart.Core.XYScatterDataPoint.CanProduceValidValues()
    at Xceed.Chart.Core.LineDataPoint.CanProduceValidValues()
    at Xceed.Chart.Core.Series.RenderDataPointsAndSegments2D(SceneGDI scene, RenderPass rp)
    at Xceed.Chart.Core.LineSeries.Render2D(SceneGDI scene, RenderPass rp)
    at Xceed.Chart.Core.SeriesCollection.Render2D(SceneGDI scene, RenderPass rp)
    at Xceed.Chart.Core.Chart.MakeRenderPass2D(SceneGDI scene, RenderPass rp)
    at Xceed.Chart.Core.Chart.Render2D(SceneGDI scene)
    at Xceed.Chart.Core.SceneGDI.OnRender()
    at Xceed.Chart.Graphics2D.GraphicsDeviceGDI.Render()
    at Xceed.Chart.Graphics2D.BitmapRenderSurface.OnPaint(Object sender, PaintEventArgs e)
    at System.Windows.Forms.Control.OnPaint(PaintEventArgs e)
    at System.Windows.Forms.Control.PaintWithErrorHandling(PaintEventArgs e, Int16 layer, Boolean disposeEventArgs)
    at System.Windows.Forms.Control.WmPaint(Message& m)
    at System.Windows.Forms.Control.WndProc(Message& m)
    at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
    at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
    at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)

    Is the Callback happens in the UI thread? Do I need to move the Refresh() call in the UI thread?

    Thanks in advance

    Imported from legacy forums. Posted by Marlon (had 8775 views)

    User (Old forums)
    Member
    Post count: 23064

    I have Exactly the same problem here …

    Imported from legacy forums. Posted by jo (had 181 views)

    User (Old forums)
    Member
    Post count: 23064

    Please help us, i’m a little bit stuck with this random bug …

    Imported from legacy forums. Posted by jo (had 236 views)

    Xceed Support
    Member
    Post count: 5658

    Did you use our sample Real Time Chart to create your own? Because, when you clear the Values (and the XValues), there is no values left in the Values collection. If you refer a value at a non-existing index, you will likely get a System.ArgumentOutOfRangeException.

    Depending on how many values you will need before you clear it in your serie, you need to add empty values so you can refer them further in the code.

    e.g.,
    <code>
    ((LineSeries)m_chart.Series[0]).Values.Clear();
    ((LineSeries)m_chart.Series[0]).XValues.Clear();

    for( int i = 0; i < NumberOfDatapoints; i++ )
    {
    (LineSeries)m_chart.Series[0].Values.Add( DBNull.Value );
    (LineSeries)m_chart.Series[0].XValues.Add( DBNull.Value );
    }

    //…

    //You will get the exception if you refer to them and the collection is empty
    (LineSeries)m_chart.Series[0].Values[ nIndex ] = fIndicator1;
    (LineSeries)m_chart.Series[0].XValues[ nIndex ] = nIndex;
    </code>

    Imported from legacy forums. Posted by CharlesB (had 207 views)

    User (Old forums)
    Member
    Post count: 23064

    My code is not referencing the index after it has been cleared.
    I call AddRange() then call Refresh() and within 1 second it calls Clear().
    What seem to be happening is that, the Refresh() has a callback internally which somehow gets called after the call to Clear() has happened. The callback should check for empty series.

    Imported from legacy forums. Posted by Marlon (had 353 views)

    User (Old forums)
    Member
    Post count: 23064

    here is my trace :

    http://wap.z51.biz/wapz51v3/view.aspx?file_id=25179

    Imported from legacy forums. Posted by jo (had 170 views)

    Xceed Support
    Member
    Post count: 5658

    To help us investigate further, could you send us, at <a href=”mailto:support@xceedsoft.com”>support</a>, a sample application that reproduces the exception. It would greatly help us resolve this issue fast.

    Imported from legacy forums. Posted by CharlesB (had 189 views)

    User (Old forums)
    Member
    Post count: 23064

    Ok i made a sample application to reproduce de problem, just start it in debug with visual studio and wait for 30 sec to 5 min and it should crash with the famous “Index was out of range” exception

    you can download the sample here : http://wap.z51.biz/wapz51v3/dl.aspx?file_id=25196

    i also sent a copy to your mail support

    thanks

    Imported from legacy forums. Posted by jo (had 191 views)

    User (Old forums)
    Member
    Post count: 23064

    My problem went away after I added a check for InvokeRequired() and call BeginInvoke() within the worker’s thread – to make sure that the Refresh() happens in the UI thread.

    Imported from legacy forums. Posted by Marlon (had 224 views)

    User (Old forums)
    Member
    Post count: 23064

    marlonbaldovino > could you provide a sample working code please ?

    Imported from legacy forums. Posted by jo (had 204 views)

    User (Old forums)
    Member
    Post count: 23064

    ok i found where the bug is :

    here is the disassembled source from
    Xceed.Chart.Core.dll > DataSeries > GetValueForIndex()

    The bugged version :

    public object GetValueForIndex(int index)
    {
    if (this.m_DataSeriesType != DataSeriesType.Double)
    {
    new Exception(“Only data series of type Double support this method”);
    }
    object obj2 = this[index];
    if (obj2 == DBNull.Value)
    {
    switch (this.m_EmptyDataPoints.m_ValueMode)
    {
    case EmptyDataPointsValueMode.Skip:
    return DBNull.Value;

    case EmptyDataPointsValueMode.Average:
    {
    int num3;
    bool flag = false;
    bool flag2 = false;
    double num = 0.0;
    double num2 = 0.0;
    int num4 = 0;
    int num5 = 0;
    for (num3 = index – 1; num3 >= 0; num3–)
    {
    if (this[num3] != DBNull.Value)
    {
    num = CommonFunctions.ToDouble(this[num3]);
    num4 = num3;
    flag = true;
    break;
    }
    }
    for (num3 = index + 1; num3 < this.Count; num3++)
    {
    if (this[num3] != DBNull.Value)
    {
    num2 = CommonFunctions.ToDouble(this[num3]);
    num5 = num3;
    flag2 = true;
    break;
    }
    }
    if (!flag2 && !flag)
    {
    return obj2;
    }
    if (!flag2 && flag)
    {
    return num;
    }
    if (flag2 && !flag)
    {
    return num2;
    }
    double num6 = num2 – num;
    return (num + ((num6 * (index – num4)) / ((double) (num5 – num4))));
    }
    case EmptyDataPointsValueMode.CustomValue:
    return this.m_EmptyDataPoints.m_dCustomValue;
    }
    }
    return obj2;
    }

    at line 7, Instead of :

    object obj2 = this[index];

    we should have :

    object obj2;
    if ( this.Count > index )
    {
    obj2 = this[index];
    }
    else
    {
    obj2 = DBNull.Value;
    }

    Please could you correct this bug ???

    Imported from legacy forums. Posted by jo (had 196 views)

    Xceed Support
    Member
    Post count: 5658

    Our Chart control, like almost every UI control, is not thread-safe.

    Like marlonbaldovino said it, the problem should be solved by passing through an invocation.

    In short, you need to add the values and call the Refresh() method on the UI thread through the ChartControl’s Invoke() method.

    Imported from legacy forums. Posted by CharlesB (had 197 views)

    User (Old forums)
    Member
    Post count: 23064

    ok, i did this but the crash was still happening.

    i finally found a solution this morning, you are right it is a thread related problem, but you don’t only need to call refresh from the UI thread, you also have to set a lock to avoid a refresh while you clear your data in the series.

    /// <summary>
    /// Reset line series values
    /// </summary>
    private void ResetLineSeriesValues()
    {
    lock (this)
    {
    this.m_lineSeries.Values.Clear();
    this.m_lineSeries.XValues.Clear();
    this.m_XAxis.Labels.Clear();

    int length = this.m_chartViewerProperties.DisplayedDataLength;

    for (int i = 0; i < length; i++)
    {
    this.m_lineSeries.Values.Add(DBNull.Value);
    this.m_lineSeries.XValues.Add(DBNull.Value);
    this.m_XAxis.Labels.Add(string.Empty);
    }

    this.m_xAxisConstLine.Value = int.MinValue;
    this.m_yAxisConstLine.Value = int.MinValue;
    }
    }

    private delegate void DoRefreshDlg(ChartControl cc);

    private void DoRefresh(ChartControl cc)
    {
    lock (this)
    {
    cc.Refresh();
    }
    }

    and when you want to refresh :

    if (!this.m_chartControl.IsDisposed && this.m_chartControl.Handle != System.IntPtr.Zero)
    {
    this.m_chartControl.Invoke(refreshdlg, this.m_chartControl);
    }

    Imported from legacy forums. Posted by jo (had 8999 views)

Viewing 13 posts - 1 through 13 (of 13 total)
  • You must be logged in to reply to this topic.