IamHuM

Hi...

In my application i am using ZedGraph graph drawing tool. I added a BackGroundWorker thread in my application... I am continuosly drawing the graph in the BackGroundWorker thread... But while drawing the graph I am getting an unexpected error.

I am handling all cross-threaded operations correctly.. Here are the details of the error...

System.InvalidOperationException: Collection was modified; enumeration operation may not execute.

at System.ThrowHelper.ThrowInvalidOperationException(ExceptionResource resource)

at System.Collections.Generic.List`1.Enumerator.MoveNext()

at ...

I used lock also for the graph control in the OnPaint event... The collection modified error is for the CurveList of the graph... I am filling the CurveList before calling the Refresh() for the ZedGraph control... I am drawing the graph from worker thread continously...

This error is not coming everytime...

Can anybody suggest me something...

Thanks in adavance,

Vinay



Re: Visual C# General Collection modified error...

Luc Vo Van

Hello,

This InvlaidOperationException is thrown whenever a collection you are currently enumerating is being modified. A typical example of this is:

Code Snippet

foreach(string s in myStringCollection)

{

if (s == "hello")

myStringCollection.Remove(s);

}

The problem is probably that ZedGraph is iterating through CurveList while, in the background thread, you are adding/removing elements from it. Try locking CurveList, or better yet, write to a temporary CurveList, and assign it to the original CurveList (in a lock block) in order to minimize the lockdown time.

Luc





Re: Visual C# General Collection modified error...

IamHuM

hi...

Thanks for the reply...

I used lock for graph component... Graph component is storing CurveList for the Graph... So wont it lock the CurveList also...

I am adding the elements in the CurveList in the BAckGroundWorker's DoWork event & Then i am calling graph.refersh(); (Here i am taking care of cross-threaded operation also...)

What is this "System.ThrowHelper"... I havnt got much information on this...

Can you suggest me anything...

Thanks,





Re: Visual C# General Collection modified error...

Sean Fowler

Don't worry about System.ThrowHelper, it's just a class that Microsoft uses for throwing exceptions within the framework.

The problem is what Luc described, a collection has been changed while it was being iterated through.

My guess is that the error occurs when the UI thread is iterating through the collection and while it's doing that the background thread adds or removes an item.

I can think of two ways to deal with this. The simplest is to iterate through the collection using a For loop rather than a For Each loop, as For loops don't have this issue. Depending on how your app works that may be fine.

Alternatively you could define a sync block around the code that iterates through the collection, and use this to prevent the background thread from changing the collection while the UI thread is iterating through it.

Sean






Re: Visual C# General Collection modified error...

Luc Vo Van

Hi again,

No, lock will only lock the object itself, and not the objects within. Also, calling Refresh is not sufficient, because Refresh is not the only call that will call Paint on your Control (actions such as overlapping the Control with another window will also call Paint), so finishing your work on CurveList before calling Refresh is not 100% safe.

ThrowHelper is just an internal utility class. What really matters is InvalidOperationException.

My suggestions are either doing your work on a temporary CurveList (as mentionned at the bottom of my previous message), or better yet, create a ZedGraph subclass that would override OnPaint, and copy the content of your temporary CurveList to the control's CurveList before calling base.OnPaint();

Luc





Re: Visual C# General Collection modified error...

IamHuM

Hi...

Thanks for yout replies...

I tried these things”­ but still I am getting the error. I tried to CurveList object also”­ but still its giving error..

Here is the code”­ Can you find out what mistake I am doing”­

//////////////////////////////////////////////////////////////////////////////

//This is ZedGraphControl.cs file

protected override void OnPaint( PaintEventArgs e )

{

lock ( this )

{

base.OnPaint( e );

try { _masterPane.Draw( e.Graphics ); }

catch (Exception ex)

{

MessageBox.Show(ex.ToString);

}

}

}

//This is CurveList.cs file.

public void Draw( Graphics g, GraphPane pane, float scaleFactor )

{

int j = 0;

lock (this)

{

CurveList cl = this;

for (j = cl.Count - 1; j >= 0; j--)

{

CurveItem curve = cl[j];

if (curve.IsBar)

pos--;

// Render the curve

if (!(curve.IsBar && pane._barSettings.Type == BarType.SortedOverlay))

{

curve.Draw(g, pane, pos, scaleFactor);

}

}

}

}

//This is in ValueHandler.cs file GetValues(...) function

lock (pane.CurveList)

{

CurveList paneCurveList = pane.CurveList; ;

foreach (CurveItem tmpCurve in paneCurveList)

{

. . .

. . .

. . .

}

}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

Can you help me with this code...

Thank you all,

IamHuM




Re: Visual C# General Collection modified error...

Luc Vo Van

It's kind of hard to understand partial code from here, but the basic idea is:

- In your background thread, fill a CurveList. That CurveList should NEVERbe used in your painting routines. Always use a clone of it when you need it.

- In your of ZedGraphControl.OnPaint override, have something like this:

Code Snippet

{

lock (theBackgroundCurveList)

{

// Creates a clone of theBackgroundCurveList

CurveList cl = new CurveList(theBackgroundCurveList);

}

// Do the rendering using cl, and not theBackgroundCurveList

base.OnPaint(e);

...

}

Luc




Re: Visual C# General Collection modified error...

Freddy Rios

Luc Vo Van wrote:

It's kind of hard to understand partial code from here, but the basic idea is:

- In your background thread, fill a CurveList. That CurveList should NEVERbe used in your painting routines. Always use a clone of it when you need it.

- In your of ZedGraphControl.OnPaint override, have something like this:

Code Snippet

{

lock (theBackgroundCurveList)

{

// Creates a clone of theBackgroundCurveList

CurveList cl = new CurveList(theBackgroundCurveList);

}

// Do the rendering using cl, and not theBackgroundCurveList

base.OnPaint(e);

...

}

Luc

Just to give some insights on the solution. You were only missing the lock on the curvelist routing. Luc solution goes further, cloning the list lets you reduce the locking time on the list.