开发者

Red X GUI crash! I almost gave up on solving it!

开发者 https://www.devze.com 2022-12-15 23:00 出处:网络
I am facing a complex bug with the Dundas Charting for Winforms tool used with MS Visual Studio 2008 C#.

I am facing a complex bug with the Dundas Charting for Winforms tool used with MS Visual Studio 2008 C#.

The following error occurs when a GUI event is raised on the Chart object while it’s invalidating. When the error happens the dundas chart shows a big X mark. ...

************** Exception Text **************

System.ArgumentOutOfRangeException: Axis Object - The Interval can not be zero
Parameter name: diff
   at Dundas.Charting.WinControl.AxisScale.a(Double )
   at Dundas.Charting.WinControl.Axis.a(Double , Double , AxisScaleSegment , DateTimeIntervalType& )
   at Dundas.Charting.WinControl.Axis.a(ChartGraphics , Boolean , AxisScaleSegment , Boolean )
   at Dundas.Charting.WinControl.Axis.b(ChartGraphics , Boolean , Boolean )
   at Dundas.Charting.WinControl.Axis.Resize(ChartGraphics chartGraph, ElementPosition chartAreaPosition, RectangleF plotArea, Single axesNumber, Boolean autoPlotPosition)
   at Dundas.Charting.WinControl.ChartArea.a(ChartGraphics )
   at Dundas.Charting.WinControl.ChartPicture.Resize(ChartGraphics chartGraph, Boolean calcAreaPositionOnly)
   at Dundas.Charting.WinControl.ChartPicture.Paint(Graphics graph, Boolean paintTopLevelElementOnly, RenderingType renderingType, XmlTextWriter svgTextWriter, Stream flashStream, String documentTitle, Boolean resizable, Boolean preserveAspectRatio)
   at Dundas.Charting.WinControl.ChartPicture.Paint(Graphics graph, Boolean paintTopLevelElementOnly)
   at Dundas.Charting.WinControl.Chart.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.WndProc(Message& m)
   at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)

The scenario is as follows:

  • I have a grid view that has the list of objects that reference the series being plotted.
  • The plot is being updated each 1 second using chart.Invoke(AddData)

This is the event that causes the crash:

private void dataGridView1_CellEndEdit(object sender, DataGridViewCellEventArgs e)
{
    if (e.ColumnIndex >= 0 && e.RowIndex >= 0)
    {
        AppDataSeries boundData =
dataGridView1[e.ColumnIndex, e.RowIndex].OwningRow.DataBoundItem as AppDataSeries;

        if (boundData.Tag != null)
        // Tag is of Type Dundas.Charting.WinControl.Series
        {
            switch (e.ColumnIndex)
            {
                case 1:
                    MUChart.Series[boundData.SeriesName].ChartArea = 
                          boundData.ChartArea.ToString();
                    // when you change the chart area of a series it
                    // crashes the chart control
                    // also when you enable or disable a series using
                    // series1.Enabled = true,
                    // it could crash the chart control
                    MUChart.ChartAreas[boundData.ChartArea].Visible = true;
                    break;

            }
        }
    }
}

The drawing is done in the following manner

A background thread is capturing

It is raising the event

OnDataAvailable every second

Here’s the handler

void serviceWrapperInstance_DataAvailable(object sender, DataAvailableEventArgs e)
{
    if (e.ViewId == currentViewId)
    {
        if (MUChart.InvokeRequired)
        {
            MUChart.Invoke((MethodInvoker)AddData);
        }

        else
        {
            AddData();
        }
    }
}
public void AddData()
{
    if (MUChart.Series.Count > 0)
    {
        for (int i = 0; i < currentViewSeries.Count; i++)
        {
            AddNewPoint(currentViewSeries[i].XValue, MUChart.Series[i],
currentViewSeries[i].YValue * ((currentViewSeries[i].IsInverse) ? -1 : 1),
currentViewSeries[i].ChartColor);

            dataSaver[MUChart.Series[i].Name].Add(new DataPoint(currentViewSeries[i].XValue,
(double)currentViewSeries[i].YValue));

        }
    }
}
public void AddNewPoint(double xValue, Series ptSeries, double yValue,
Color pointColor)
{
    try
    {
        ptSeries.Points.AddXY(xValue, yValue);
        if (draggedDroppedSeriesMapper.ContainsKey(ptSeries))
            foreach (Series item in draggedDroppedSeriesMapper[ptSeries].DraggedDroppedSeriesVersions)
                item.Points.AddXY(xValue, yValue);
        MUChart.Invalidate();
        // if I remove the previous line the pl开发者_如何学Goot doesn’t crash, but doesn’t update !!
    }
    catch (Exception ex)
    {
        Logger.Log(TraceLevel.Error, "AddNewPoint()", ex);
    }
}

The interesting thing about this bug is that it doesn’t happen on all machines. I noticed that it happens on high specs machines like our 8 core CPU DELL machine, and a new quad core laptop that we got here. This raised the suspect of a threading problem; however, threading seems to be okay since the chart object is accessed from the same main thread.

Please help me with this

UPDATE the assignment using the setter that takes place in the function dataGridView1_CellEndEdit MUChart.Series[boundData.SeriesName].ChartArea = boundData.ChartArea.ToString(); calls chart.invalidate internally, while the invoked function 'AddData' that updates that chart calls it explicitly. I read in the MSDN library that "control.invalidate" doesn't force a synchronous paint unless control.update is called after it. I am almost certain that the conflict is happening upon invalidation even though everything is happening on the same thread since the redrawing is taking place asynchorounsely . I understood what's happening this way but I don't know how to avoid it. control.update is doing me no good.

ChangeTheChartConfigurations(); DrawTheChanges() ---- >>>> this works asynchronously UpdateDataPoints() DrawTheChanges() ---- >>> this works while the first change has still not occured yet. For example, the series might have been moved to a difference chart area and Dundas.Charting.WinControl.AxisScale.a(Double ) (the last function in the stack trace)is being called on already hidden chart area.. that's just a thought

UPDATE

I logged the thread ID from both the event handler and the AddNewPoint function and it was the same as the main thread's


Rather than invalidating the chart after drawing each point, draw all the points and then invalidate the chart once. Try moving MUChart.Invalidate() from AddNewPoint() to after the for loop in AddData().


I think you should not call

MUChart.Series[boundData.SeriesName].ChartArea = boundData.ChartArea.ToString();

directly. You should wrap it to InvokeRequired/Invoke code. And the same for

MUChart.ChartAreas[boundData.ChartArea].Visible = true;


You probably have a cross thread operation.

Try placing your MUChart.Invalidate after all points have been added and in the main GUI thread.

The red cross is normally drawn by the .NET framework itself when you access a UI control from another thread than the UI thread.

The cross is also displayed when a resource is not available or an exception occurs in the Control be displayed.


I suspect you have an object that has been disposed. (This is the kind of odd exception you get.) An indication is your statement that it happens on high-end machines. Meaning, the machine performed garbage collection.

1) What happens when you call Refresh instead of Invalidate?

2) Remove the long chain of object references and create variables. You understand that each {} creates a local scope (stack). Which may cause an object's scope to change while the chart is updating. Do Series s = MUChart.Series[boundData.SeriesName]; ChartArea a = s.ChartArea; and so on.

3) Try a lock or synchronizing object in AddNewPoint or AddData. Also possible what you are doing is not thread-safe for the control. Check documentation.

4) I would not place the try:catch in AddNewPoint. Better is to place it outside of the for loop, but in your case put it in the loop.

5) You really shouldn't be using calls to Refresh, Update and Invalidate. It is an indication your implementation needs to be less CPU cycle heavy. (For example, removing the try:catch, addressing any multiple "Data Available" events, using a sync lock, remove the AddNewPoint method.)


Please focus on following error:

The Interval can not be zero Parameter name: diff

I think the two values for the x axis of chart is having same value hence the exception is raised .


I met the same error and found the reason is I set AxisIntervalMode as VaiableCount even when there is no data. So I dynamically change the AxisIntervalMode according to if the series has data. Hope this may help someone.

0

精彩评论

暂无评论...
验证码 换一张
取 消