How to Update a C# chart control using databinding - c#

I have a C# Chart Control and I data bind it like so.
chart.Series[0].Points.DataBindXY(xAxis, yAxis);
where xAxis is List<String> and yAxis is List<Double>
On another thread xAxis, and yAxis are constantly updated (Multiple calls to .Add())
However the chart does not update unless I call DataBindXY() again, however this seems to cause issue because every once and a while i get
Error: "Collection was modified; enumeration operation may not execute."
Which at some point causes my program to crash with
Error: "system.reflection.targetinvocationexception' occurred in mscorlib.dll"
-Is there something im missing as far as updating? or should I be doing this diffrently, let me know if you need more information.

You need to add locking or synchronization both in your update method and your DataBindXY method. You cannot modify a List and read it at the same time, because operations on Lists are not thread safe.
I'd recommend reading this (or one of the many, many other) introductions on thread synchronization in C#: http://msdn.microsoft.com/en-us/library/ms173179.aspx
EDIT: Here is an example of how to do this:
Object lockOnMe = new Object();
... in your Add loop
(int i = 0; i < dacPoints.Count; i += 1) {
TimeSpan span = new TimeSpan(0, 0, i + 1);
lock (lockOnMe) {
presenter.addPoint(span.ToString(), dacPoints[i]);
}
System.Threading.Thread.Sleep(200);
}
... when calling DataBindXY()
lock (lockOnMe) {
// Note that I copy the lists here.
// This is because calling DataBindXY is not necessarily a serial,
// blocking operation, and you don't want the UI thread touching
// these lists later on after we exit the lock
chart.Series[0].Points.DataBindXY(xAxis.ToList(), yAxis.ToList());
}

The chart control reads the datasource once (when you issue the DataBindXY call) which is the reason why it is not updating when you modify the collection.
The reason you are getting the occasional issue is because your background thread doing the update is changing the collection as the chart is reading from it.
You may be better off having the chart axis as an ObservableCollection created on your UI thread. You can then respond to the CollectionChanged event to instruct the Chart to DataBindXY.
HOWEVER, in order to use this correctly, your background threads will need to invoke the add calls to the collection on the UI thread. If you have a reference to the chartcontrol you can use the control.BeginInvoke call.

Related

How to implement Dispatcher BeginInvoke for simple parse statement?

I have a background thread and a main thread for a loop in my C# application.
On this background thread I have to access Text from the UI and Parse it to be used in conditional statements as seen below:
int i = 0;
int loopNum = int.Parse(Loop_Number.Text); //< What I need to access
if (loopNum > 0)
{
while (i < loopNum)
{
//DoStuff
}
}
Problem being the above statements are being run in the background thread not the main UI thread. So the program throws an InvalidOperationException for that int.Parse statement.
Exception has this information:
Additional information: The calling thread cannot access this object because a different thread owns it.
So being that I only have a UI thread and this thread run temporarily how can I access Loop_Number.Text within this background thread.
I have tried the below code:
Dispatcher.BeginInvoke((Action)(() => { int loopNum = int.Parse(Loop_Number.Text); }));
but then loopNum is out of context for the conditional statements.
The short answer, which will resolve the immediate problem, is that you need to use Invoke() instead of BeginInvoke():
string text = (string)Dispatcher.Invoke(() => Loop_Number.Text);
int loopNum = int.Parse(text);
Now, that said, there are signs in your question that you're simply doing it all wrong. :(
First, you should not need to access a UI object directly at all. I.e. from the exception you report, the Loop_Number variable presumably is a reference to some WPF control, like a TextBox. Instead, you should be following some kind of data binding pattern, like MVVM, in which you have a non-UI object which has a property bound to the UI control, and which receives the desired number.
Doing it that way, not only would the property be set in the UI thread, allowing the value to be accessible elsewhere (such as in a different thread) without invoking on the UI thread, you can make the property have the type int and WPF will convert from the text for you. You wouldn't even need to call int.Parse() at all.
Second, while it's hard to say for sure without a good Minimal, Complete, and Verifiable example showing clearly what you're doing, I doubt it's really a good idea for the thread to be retrieving the value at all. Instead, the value is something that your UI thread should have retrieved at the point in time that it decided it needed to start this background operation, and the value should be passed to the background operation somehow (stored in a data structure specific to the operation, passed to an async method that represents the operation, etc.).
Bottom line: while the code at the top of this answer will address your specific concern, I doubt it's really the way this code (whatever it is) ought to be written. You should probably consider revisting your design, so that it works more smoothly, without the need for calling Invoke() at all.

WPF How to update GUI when background thread finishes creating collections?

So I want to load my data collections in a background thread and then bind my treeview to the new collection (rather than having the background thread queue things up on the dispatcher every time it wants to add an item to the list [sounds inefficient]).
Is this possible? I create the new data structure and it is output as pd.result on a background thread. When the UI thread checks that the dialog box has closed, it should then set
ModuleHierarchyVM.TopLevelModules = pd.Result as ObservableCollection<ModuleViewModel>;
after this the event OnLoadVCD is called. I have an event handler that then tries to set
a treeview's itemsource to the new collection.
this.AvailableModulesTreeView.ItemsSource = gvvm.ModuleHierarchyVM.TopLevelModules;
This crashes with the error:
"The calling thread cannot access this object because a different thread owns it."
Not even sure how to debug it, the call stack doesn't give any real details.
However, if I just set the Itemsource to a blank new empty collection like so:
this.AvailableModulesTreeView.ItemsSource = (IEnumerable<object>)new List<object>();
it doesn't crash (but then it's not displaying my data either). Any ideas what could be causing the crash?
I thought it might be that I was updating the UI from the wrong thread, so I tried both calling the dispatcher with begininvoke, and checking that I am indeed the UI thread with dispatcher.checkaccess(). so that does not seem to be the issue. However, I really don't know what is going on.
Another way I could implement this is to just make my parsing routine just update the original data structure that is bound to the treeview by caling dispatcher on each item as it is added to the observable collection. However, even if that is the only solution, I really dislike not knowing why something doesn't work. In my mind, it seems reasonable to just create an entirely new data structure on a different thread, and then rebind the new datastructure to the treeview, discarding the old one. It also seems cleaner to me than dozens of 1 line ObservableCollectionInstance.Add calls being placed on the dispatcher while parsing through a file on the background thread.
Full Code:
method called by UI thread
public bool LoadPortInterface(string VCDFileName)
{
ProgressDialog pd = new ProgressDialog("Loading File: ", VCDFileName);
pd.Owner = Application.Current.MainWindow;
pd.WindowStartupLocation = WindowStartupLocation.CenterOwner;
ModuleHierarchyVM.TopLevelModules.Clear();
VCDData TempVCDOutput = null;
Func<object> handler = delegate
{
return VCDParser.ParseVCDFileForAllPorts(VCDFileName, this, pd.Worker, out TempVCDOutput);
};
pd.RunWorkerThread(handler);
pd.ShowDialog();
if (pd.DialogResult == true)
{
ModuleHierarchyVM.TopLevelModules = pd.Result as ObservableCollection<ModuleViewModel>;
VCDOutput = TempVCDOutput;
}
OnLoadVcd();
}
Response to OnLoadVCD event handler in graphviewer:
void gvvm_LoadVCDEvent(object sender, EventArgs e)
{
this.AvailableModulesTreeView.ItemsSource = gvvm.ModuleHierarchyVM.TopLevelModules;
}
I think it will be easer to use TPL "Task Parallel Library"
For example, if you want to create new thread you can create it as task.
var task = Task.Factory.StartNew(() =>
{
// write what you want to do here
}
You can get more examples from this link Task Parallelism (Task Parallel Library)
So, to update the UI from thread, you can run this thread in the same of UI thread as below:
var uiContext = TaskScheduler.FromCurrentSynchronizationContext();
_taskFactoryWrapper.StartTask(() => DoSomeWork(viewSettings.AnyValue)).ContinueWith(task =>
{
viewSettings.Result= task.Result;
},TaskContinuationOptions.AttachedToParent)
.ContinueWith(t => EndingUploadingProgress(viewSettings), uiContext);
So you can create a TaskScheduler associated with the current UI thread.]
You are most likely creating, reading or modifying the ObservableCollection on a thread other than the UI thread. Also make sure you are not adding to or removing from the ObservableCollection on anything other than the UI thread.
To debug this, put a breakpoint wherever you access/modify the observable collection and note the thread number (Thread window in VS) that hits that breakpoint. It should always be the same.
You could use another structure (List/Array) to hold the results, then call back to the UI thread to update/create the ObservableCollection. Updating the ObservableCollection is inexpensive, even for hundreds of items.
What does get expensive is that ObservableCollection will raise a change event on every change, which may be handled by the UI components to change their layout, which has to be done on the UI thread anyway. This UI event handling is why ObservableCollection prevents you from modifying across threads.
If you are adding/removing a large number of items, it may be better to create a new collection and reassign the DataSource. If you always do this, you can use a List instead. ObservableCollection is for when you want to modify the list and have the control only change the smallest amount possible. Changing the DataSource (eg to List) will clear the control and rebuild it, which may be better for many changes.
See:
Updating an ObservableCollection in a separate thread
How do I update an ObservableCollection via a worker thread?
What's the best way to update an ObservableCollection from another thread?

Is there any way to use an object across two STA threads in WPF?

Okay, before anyone starts up, let me be clear...
I'm using a custom control, in this case Telerik's RadPane. When you deserialize these panes they come in through a callback method, and thus, are ALL owned/deserialized by one thread, and they cannot be cloned or deep copied, etc. so there are some huge limitations I am trying to work with. This is bad because, I want to add content to each pane but want them all to work within the confines of their own UI thread. For example, I want to add a list to a pane and have it update with millions of records, etc. but if I update one pane, the UI thread will lock up for all panes.
I need to create the contents of this object in a different thread, but the problem is, when I try to set the content, the obvious issue is that the object is not owned by the same thread that owns my pane:
myRadPane.Content = myGrid;
myGrid is owned by a different thread than myRadPane, so it doesn't want to set the content. Is there any way around this problem in WPF or a way to allow one object to be "shared" across UI threads?
Edit: Please note, I am fully aware of how dispatchers work, and my application makes use of them whenever the main UI thread needs updating for one pane. However, in this case, even while using dispatchers as minimally as possible, the UI thread bogs down under heavy load.
You CAN copy WPF controls.
Call this in your creater Thread:
public string CopyWPFControl<T>(T source)
{
string childXaml = XamlWriter.Save(source);
var stringReader = new StringReader(childXaml);
return stringReader.ToString();
}
Transfer your result to your Dispatcher / STA thread and call:
public T RecreateWPFControl<T>(string source)
{
var xmlReader = XmlReader.Create(source);
var clonedChild = (T)XamlReader.Load(xmlReader);
return clonedChild;
}
So create your object in a Diferent Thread, then copy the Control and all its Bindings ect and use the output to display.
imported this will only copy the XAML description.

Thread safety, lists, binding and WPF

I have a WPF ListView that is bound to a collection (List<T>). It is currently updated from the current thread which works ok.
I want to move the logic for updating the list into a thread and I see some potential issues regarding thread safety and the binding of the list. Can I be assured that the binding will not be updated unless I call NotifyPropertyChanged? Is NotifyPropertyChanged a blocking call or does it just add it to a message queue; in this instance surely there may be a condition where I tell the ListView the collection updated, but by the time the binding updates I may be modifying the collection in the other thread which will throw an exception or crash the program.
What is the best method for implementing thread safety in such a scenario?
INotifyPropertyChanged is not thread safe, and it does block the calling thread.
Best? That's A good question. I dunno. The bottom line is that, at some time or another, calls must be marshalled onto the UI thread. When do you do this?
You could 1) prepare everything, then deliver it to the UI thread where the UI is then updated. Or, you could 2) implement INotifyPropertyChanged and make the firing of that event always happen on the UI thread, or 3) you could do one of a number of different things.
Usually, however, you would want updates to the UI to happen all at once (not one at a time, as you would get when adding single items to an ObservableCollection, for instance). So it might be advisable to make some thread safe base classes that implement INotifyProperty and CollectionChanged and use these.
There is nothing in the framework that will do this for you, unfortunately.
You can make the collection update from Dispatcher.Invoke to avoid those threading problems:
void ThreadProc()
{
window.Dispatcher.Invoke(() => {
//UpdateList
});
}

C#: Populating a UI using separate threads

I'm trying to make some sense out of an application Ive been handed in order to track down the source of an error. Theres a bit of code (simplified here) which creates four threads which in turn populate list views on the main form. Each method gets data from the database and retrieves graphics from a resource dll in order to directly populate an imagelist and listview.
From what Ive read on here (link) updating UI elements from any thread other than the UI thread should not be done, and yet this appears to work?
Thread t0 = new Thread(new ThreadStart(PopulateListView1));
t0.IsBackground = true;
t0.Start();
Thread t1 = new Thread(new ThreadStart(PopulateListView2));
t1.Start();
Thread t2 = new Thread(new ThreadStart(PopulateListView3));
t2.Start();
Thread t3 = new Thread(new ThreadStart(PopulateListView4));
t3.Start();
The error itself is a System.InvalidOperationException "Image cannot be added to the ImageList." which has me wondering if the above code is linked in some way.
Iis this method of populating the UI recommended and if not what are the possible complications resulting from it?
Update:
I may have given some misinformation by referring to a 'form'. The application is a windows forms application but the code is from a plugin application based on a usercontrol. The threads are created inside an initialise method publically exposed by this control. The listviews etc are also part of this plugin usercontrol.
DO NOT USE threads for that - if you have to do that async, use WOrkItems on a THreadPool. Thread usage in general should be reseved for long running items - a THreadPool or the new .NET 4.0 tasks API are way better suited for that.
UI elements should only be namipulated from the element creation thread. It "works" or not depending on what version of the .net framework you use or what the control really is if you break this.
Within your threads methods, such as DoWork() for the BackgroundWorker class, for example, you will need to instiate a delegate method to populate your UI control. Then, verifying whether your UI control requires to be invoked (InvokeRequired property), then invoking it when it'srequired to.
private delegate IList<MyObject> PopulateUiControl();
private void myThread_DoWork(object sender, DoWorkEventArgs e) {
PopulateUiControl myDelegate = FillUiControl;
while(uiControl.InvokeRequired)
uiControl.Invoke(myDelegate);
}
private IList<MyObject> FillUiControl() {
uiControl.Items = myThreadResultsITems;
}
It is not a precise working code, as I can't take the time to do the research, etc. but this shall put you in the path to succeed.
In the end, I agree with the others, try to avoid such things in the future, as it can get tricky to debug or reveal some strange behaviour. Perhaps .NET 4 has some improvements on the topic as Microsoft has worked hard to make parallelism easy for the use of multicore processors for developers.
As others have stated, you cannot update your UI from any thread other than the one it was created by.
If a thread wants to update the UI, it needs to invoke a method on the UI control on the same thread that created it using BeginInvoke.
PS: I am assuming when you say UI, you mean a WindowsForms and not WPF. I have used the above solution successfully in WinForms.
Update: Here are a couple of articles that explain the concept in-depth:
* Threading in Windows Forms
* WinForms UI Thread Invokes: An In-Depth Review of Invoke/BeginInvoke/InvokeRequred
Also, related question from SO: In WinForms, why can't you update UI controls from other threads?
Never update UI from a worker thread. Program may work sometimes, but this is undefined behavior. Add this line to the program initialization code:
Control.CheckForIllegalCrossThreadCalls = true;
After this every incorrect UI update attempt fails, allowing to fix all errors in the code.
If this is done because getting the values fr the listviews takes time, then get the values in a background worker and then use the main thread to bind the data to the listview.

Categories

Resources