Problem Interact with the UI thread

May 9, 2012 at 8:19 AM
Edited May 9, 2012 at 8:26 AM

Hi,

I'm working on NodeXL to visualize large dynamic networks.

Briefly, my network is loaded on Disk by a Berkeley Database and it is builded like a binary tree.

However, I added a new Slider (very similar to the Dynamic Filters Sliders) into the Task Pane. And by adjusting the slider NodeXL is able to show the evolution over time of the network. 

Each time I adjust the slider: I query my BDB; get the results from the query; then update the workbook by the results and afterwards update the TaskPane according to the workbook.

I also added a play button to see the evolution automatically.

When I push the play button the "Time Slider" will slide in a automatic way by "DynamicFilterRangeTrackBar.SmallChange". The play button works in a way that between each query (which includes the workbook and taskPane update), it will be created a thread, which will wait for a certain period(1sec for instance). And after this second will call asynchronously the next query (by the "BeginInvoke" method), which always includes graphics update.

System.Threading.Thread.Sleep(3000);

try

                {

                    caller = new CallBackMethodCaller(QueryTreeDB)

                    this.BeginInvoke(caller, null);

                    System.Threading.Thread.CurrentThread.Abort();

                }

                catch (Exception oException)

                {

                    System.Threading.Thread.CurrentThread.Abort();

                }

 

So it will be showed the network's evolution like a video.

Everything works for networks of 100 edges over 100 time slices.

Everything works also for a network of 3000 edges over 900 time slices except for the play button. When I push the play button in this case NodeXL crashes after the first query (which always includes the first workbook and taskPane update). I think it's a problem of the  asynchronous call. So how could I interact with the UI NodeXL thread from the callback method? 

Any help could be appreciated.

Thanks in advance.

May 9, 2012 at 6:26 PM

I don't understand your threading scheme, but the fact that you're routinely calling Thread.Abort() tells me that there is something wrong with your design.  Aborting a thread shouldn't be necessary under normal circumstances.

There are many ways to do multithreading nowadays in .NET, but in Windows Forms applications an easy approach is to use the System.ComponentModel.BackgroundWorker class.  It allows you to perform a task on a background thread that it creates, and it manages all the potentially hazardous interaction between the background thread and the UI thread in a simple, clean, reliable manner.  NodeXL uses it for laying out the graph in the background without locking up the UI, and there has never been a threading bug in NodeXL that I'm aware of.  (Threading bugs are the absolute worst kind, in my experience.)  Take a look at it and see if it might suit your needs.

-- Tony

http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx

 

May 9, 2012 at 6:39 PM

And be sure to avoid accessing the UI from the background task, which is prohibited in Windows Forms.

-- Tony

May 9, 2012 at 9:19 PM

Thank you for your prompt reply and suggestions Tony.

I'm sure that I'm not accessing the UI from the background task. The background task just assign a new "job" (that is an execution of a method) to the UI thread by calling the "BeginInvoke" method. 

Could you please tell me which is the NodeXL threading scheme?

 

However is it right that all the UI objects in NodeXL comunicate each other by "Commands"?

 

Thanks in advance.

Regards.

--Jacopo

May 9, 2012 at 11:02 PM

Jacopo:

NodeXL uses threading sparingly, in the few places where an operation can take a long time and I don't want it locking up the UI.  These include laying out the graph, calculating graph metrics, and creating subgraph images.  In each case, it uses a BackgroundWorker object to do the work on a background thread.  NodeXL doesn't call Control.Invoke() and it doesn't use any other threading mechanism.  You have to be very careful with threading, which is why I chose the simple BackgroundWorker technique for NodeXL.

The program uses a "chain of responsibility" pattern to send commands among the TaskPane, Ribbon, and Workbook.  That's separate from threading, though.

-- Tony

 

May 10, 2012 at 7:52 AM
Edited May 10, 2012 at 10:59 AM

Thank you again for the reply Tony.

I already knew that commands are separate from threading, it was another question, I was just curious :)...Thanks!

So if you use BackgroundWorker to (for instance) laying out the graph, how dou you match UI objects with objects that you are using on the background task. I know how to do that with WPF Data Binding. In other words: let's suppose we have a ListBoxitem derives from Control so it is considered part of the GUI. And we want to manage this ListBoxItem from the backgroundWorker. The obvious solution is build up a list x of Content (strings) and delay the creation of the Items to the Completed event. Then we could consider a mini ViewModel for the items and we need just to bind a list to the ItemsSource.

How does that work in WinForms and then in NodeXL?

Thanks in advance.

Regards.

--Jacopo

May 10, 2012 at 4:58 PM

Jacopo:

I do the same thing that you're describing.  In the layout case, the BackgroundWorker is given a Graph object, which is just a collection of vertices and their edges, and the background thread lays out the vertices in the Graph object without ever touching anything in the UI.  Then, in the BackgroundWorker.RunWorkerCompleted event handler, which runs on the UI thread, the updated graph is shown in the GraphPane and the vertex coordinates are copied to the workbook.

In general, you provide the BackgroundWorker with some data to work on, wait for the RunWorkerCompleted event, and update the UI with the data in the event handler.

-- Tony

May 11, 2012 at 8:00 AM

Thank you Tony. So WPF Data Binding is just much more flexible than the Windows Forms Binding, they are just driven from the same core issue.

 

I have another problem, not really related with the previous one:

I'm using a play button to visualize, my network evolving over time, like a video. That means I update the graph for each time slice automatically, when the play button is pushed. And since I'm handling road network, "Harel Korel Fast MultiScale" suits my needs better than the other methods (included in NodeXL) for drawing graphs. But each time the graph is updated the vertices positions change. Could you advice me a way to avoid that?

Thank you in advance for your reply.

Regards.

--Jacopo

May 11, 2012 at 6:39 PM

Jacopo:

If you set the layout algorithm to "None," the vertices won't move when you update the graph.  There are a few ways to change the layout algorithm, and I don't know which is appropriate for your application:

1. To change it in the Ribbon, set NodeXL, Graph, Layout to "None."

2. To change it programatically from within the TaskPane, set m_oLayoutManagerForToolStripSplitButton.Layout to LayoutType.Null.

Or if you don't want to mess with layouts, an alternative is to "lock" the vertices in place.  To do this, add a ReservedMetadataKeys.LockVertexLocation key to each of the graph's vertices and set the key's value to true.  This will stop Fruchterman-Reingold from moving the vertices.

    foreach (IVertex vertex in oNodeXLControl.Graph.Vertices)
    {
        vertex.SetValue(ReservedMetadataKeys.LockVertexLocation, true)
    }

-- Tony

May 14, 2012 at 12:36 AM

Thank you Tony, that was exactly what I needed.

Btw I have been realizing that there isnt a WinForms DataBinding (like the Data Binding explained here http://www.codeproject.com/Articles/38772/Safe-WinForms-Databinding ). For instance, for layout the graph like you said: "you provide the BackgroundWorker with some data to work on, wait for the RunWorkerCompleted event, and update the UI with the data in the event handler". So you don't create a DataBinding between the collection of vertices (and their edges) with the UI elements. Is it right what I'm saying?

 

Thanks in advance.

Regards.

--Jacopo

May 14, 2012 at 4:43 PM

Jacopo:

That is correct: NodeXL does not use data binding between the edges and vertices and the UI elements.  I used data binding sparingly in NodeXL, only because other techniques seemed more appropriate to my needs.

-- Tony