I am having issues on how to get around the following problem.
I have a class which which allows me to dynamically update and display a graph on a form. In order to update the values of the graph I have a method within the class of the form. I pass in the value to update the graph with to this method. Here is a high level example of what I am trying to do:
class GUICLass : Form {
//Code for drawing chart etc all here
public updategraphWithNewValue(double value){
// Code to update the graph
}
}
My other class is as follows:
class ValueProviderForGraph{
GUIClass graphForm = new GUIClass();
public calculateValuesAndPlot(){
for(int i = 0; i < 4000; i++){
graphForm.updategraphWithNewValue(i);
graphForm.update();
}
}
}
Now the issue I get from the above is that the form freezes while this operation is taking place. How could I go about getting around this, any help or advice would be much appreciated. I hope this high level example has enough information, if not please do let me know and I shall try and explain myself better.
Thanks.
Look into using BackgroundWorker. Its event-oriented interface should get you up and running quickly.
You can only update your form's controls from the thread that the control was originally created on.
If you are trying to update it from a different thread, you must marshal that call. There are numerous approaches on the web. My personal favorite (for WinForms) is the following:
https://stackoverflow.com/a/709846/141172
UPDATE
After re-reading your question at the urging of #StenPetrov, I suspect that you do not have a cross-thread issue after all, but that the UI thread is simply not processing messages while it updates the graph (this would cause the freezing during the operation).
If the UI thread is busy updating the graph, you will get that behavior. However, it looks like you are causing the graph to do an unnecessary update 3999 times. Try moving the line
graphForm.update();
outside of your for loop.
While I don't know exactly how your graph control works, I suspect the call to update() causes the entire graph to be re-rendered. You should only do that after all new data points have been added.
Do you need to update the UI often or not?
If you don't have to update the UI often, such as loading a large graph for viewing, then BackgroundWorker will do.
If you need the UI updated frequently you have to (a) make your graph calculations independent of UI, (b) run the graph calculations in a separate thread and (c) after the update is calculated use Form.Invoke(...) to update the UI
Related
This question has been asked a lot, and I've read all the posts on Stackoverflow on it.
I wanted to confirm my conclusion though.
Where the source of information being populated is quite fast (ie it doesn't take much processing time to get the information itself), it seems to me that the processing that takes the time is actually feeding it into the DataGridView. However that has to be done on the UI thread as that's where the control is.
If that's the case there seems to be limited benefit to trying to do anything in the background, and that the corollary is that there's no effective way to populate a DataGridView without bunging up the UI thread. Is that right?
Surely there must somehow be a way to populate a DataGridView entirely asynchronously while the user can still interact with the UI?
Surely there must somehow be a way to populate a DataGridView entirely asynchronously while the user can still interact with the UI?
There are many ways it can be done.
Paging via Async/await Inlined with Regular Code
If whatever it is you are fetching the data from (whether it be a direct database connection; REST or WCF call) supports paging, then you could fetch pages of data via inline async/await and add rows for each item returned in the page.
e.g.
// somewhere in your UI code
async Task LoadAsync(List<Page> pages)
{
foreach (var page in pages)
{
var stuff = await service.GetMovieSalesPagedAsync (page);
foreach (var item in stuff)
{
_dataGrid.Rows.Add (/* convert item then add it here */);
}
}
}
This is faster than say requesting all the data in one go and then trying to add rows for each item. The latter would just block the UI.
The benefit of the above approach is that the code is inline and easier to read.
Dedicated Task with Progressive Filling During Application Idle
This technique is better for when there is a large amount of data to display and you want the best performance UI-wise. It's also useful if the source does not support paging.
Here you can spawn a Task whose job is to retrieve the data a page (or all at once) at a time, then add each page results to say a ConcurrentQueue<> for the benefit of the UI thread. If you have to retrieve all of it, then break the results into pages manually.
Meanwhile, in your Application.Idle handler try to pop an item off the queue and if one is found, add the new items as rows to the datagrid. Now depending on your app, you may choose to process all available pages or wait for the next application idle event. It might take a bit of fine tuning. Waiting for the next application idle allows your app to play nice with UI responsiveness.
This will cause the datagrid to be filled progressively rather than all at once.
A con of this approach is that the code is no longer inline. You have one block of code responsible for fetching and storing data; another for pumping it into the UI.
sorry if this is a silly question, I am new to C#, so please give me a break.
I am working on Revit API. currently, Revit API doesn't support multi-threading operations.
my question is how to stop a loop without calling a new thread?
I am trying to get a snapshot and I am waiting for the user to pick a snap, so I put an infinite loop till the condition meets
while (!Clipboard.ContainsImage()) //loop till user get a clipboard image
{
}
but what if I want to abort this???
I have tried
private void Abort_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Escape)
{
abort = true;
}
}
but this method requires threading ....any suggestions!!
You want to look into asynchronous processing patterns in Revit - check for instance The Building Coders description of IExternalEvent.
It might take a little while to wrap your head around it, but let me try to explain:
Split your code up into two parts: The first part runs up until the moment you ask the user to pick a snap (I'm assuming this happens in another application?). In a separate thread, poll the clipboard (or register your application as a clipboard viewer) in your while loop. Don't forget to Sleep() while you're polling! As soon as your polling thread finds a picture, create an instance of an IExternalEvent subclass and post it to Revit. When Revit has some spare time, it will execute the Execute method, passing in a UIApplication that you can use to do the rest of your code.
Revit doesn't let you access it's API from multiple threads at the same time, but during the Execute method of an IExternalEvent subclass, you are guaranteed to be in the correct thread for running code.
You can also look into the OnIdling event - basically, this gets called each time Revit has some spare time too. So instead of using a while loop in a separate thread, you could just place your clipboard checking code in the event handler for the OnIdling event and create a mechanism (a flag) to tell you wether you are currently waiting for a snap or not...
I know I'm having a massive derp moment here and this is probably quite easy to actually do - I have had a search around and read a few articles but i'm still struggling a little, so any feedback or pointers to useful resources would be greatly appreciated!
Anyway I have a class called PopulateDatagridViews which I have various functions in, one of which is called ExecuteSqlStatement, this function is simple enough, it initializes an SQL connection and returns a DataTable populated with the results of the SQL query. Within the same class I also have various functions that use string builders to build up SQL statements. (Not ideal, I know.)
I create a PopulateDatagridViews object in my GUI thread and use it to set various datagrid views with with the returned DataTables. For example:
dataGridViewVar.DataSource = populateDgv.GetCustomers();
Naturally a problem I'm having is that the more data to be read from the database, the longer the U.I is unresponsive. I would like to shift the process of retrieving data via the PopulateDatagridViews to a separate thread or BackgroundWorker so as prevent the main GUI thread from locking up whilst this is processed.
I realise I can create a BackgroundWorker to do this and place in the DoWork handler a call to the appropriate function within my PopulateDatagridViews.
I figure I could create a BackgroundWorker for each individual function inside my PopulateDatagridViews class, but surely there is a more efficient way to do this? I'd very much appreciate a point in the right direction on this as it's driving me around the bend!
Additional Info: I use version 4.0 of the .Net framework.
I strongly suggest that you use TPL (Task Parallel Library) http://msdn.microsoft.com/en-us/library/dd537609.aspx
In your case you will create first task to pull some data and than start second task after first is completed to update UI.
I`ll try to find code that i write for similar problem.
Edit: Adding code
Task<return_type> t1 = new Task<return_type>(() =>
{
//do something to take some result
return some_result; //return it
});
t1.Start();
Task t2 = t1.ContinueWith((some_arg_that_represent_previous_task_obj) =>{//ContinueWith guarantees that t2 is started AFTER t1 is executed!
//Update your GUI here
//if you need result from previos task: some_arg_that_represent_previous_task_obj.Result //Your dataset or whatever
}, TaskScheduler.FromCurrentSynchronizationContext()); //VERY important - you must update gui from same thread that created it! (you will have cross thread exeption if you dont add TaskScheduler.FromCurrentSynchronizationContext()
Hope it helps.
Well in that case I recommend reading this msdn article to get some ideas. Afterwards you should look for some tutorials, because the msdn is not the best source to learn things. ;o)
I am a bit new to threading (not new to C#, just haven't done much threading). Can someone explain to me why this does not work?
I have a thread which calls a method I will call "Loop". Loop contains a while loop which will continuously run, and on every loop of the while I want it to check if the A Key is down (using Microsoft's Keyboard class within the XNA Framework). But for some reason it never registers that anything is being pressed.
static Thread thread = new Thread(Loop);
static bool abort = false;
public static void Begin()
{
thread.Start();
}
private static void Loop()
{
while (!abort)
{
if (Keyboard.GetState().IsKeyDown(Keys.A))
Console.WriteLine("A pressed.");
}
}
Might anyone know why the Console.WriteLine() is never being called?
EDIT:
I guess I should explain a little bit. What I am actually trying to do is create something similar to ActionScript's events in C#. So I want to pass a "condition" and an "action" to call if that condition is met in this separate class which contains this thread. What this would do would allow me to just add "event listeners" to objects and it would automatically constantly check if one of the events gets triggered, rather than leave it to me to write If statements in code to check for the events.
Upon trying to do so, the first thing I tested was regarding this XNA Keyboard stuff, because it was one of the reasons I originally wanted to build this system, but it didn't work. So I created the standalone code which i posted above to see if I had made an error in my previous code and it still didn't work.
I never use XNA so I didn't really "know" but I've run into similar situations where you can't get keyboard (and other) input from a worker thread. I googled and found that in XNA this does seem to be the case. See this for example
So you need to (and probably want to) process your game input in the GUI thread. Just checking for input on each update tick should be fine. I doubt even if it did work, you would gain any performance - and you might introduce some interesting synchronization bugs ;-)
It does look like your creating your worker thread properly - this just isn't an application for it.
OK, please disregard what has gone before. I'm not getting the errors anymore, so it seems my problem is with getting a Chart to update when I change the values to which the Chart is data bound.
//Disregard below here
Hi all. I have a WinForms application that has a panel, panel1. A background thread creates some other controls that then get added to panel1 like so
panel1.Controls.AddRange(myArrayOfControls);
This works great and I can see my controls get added. But, when new data comes in on another thread, I update values in the controls' parent objects and then need to Refresh() to get the display to update with the new values. Calling Refresh() in either context, the thread where the data comes in or the objects that receive the updated data causes an InvalidOperation exception because Invoke is required. I've tried using Invoke in my model objects and also the thread where the data is incoming and can't seem to shake the error.
If anyone has some guidance I'd greatly appreciate it.
UPDATE: Here's a little more info. I didn't think it would require it, but I was wrong. :)
I have an object class MyObject. This MyObject class gets created in a thread called topologyThread. Data comes in on dataThread. Instances of MyObject have a Panel instance variable and the Panel has child Controls including two Charts from the System.Windows.Forms.DataVisualization.Charting namespace. So, as data comes in on dataThread, I update the respective data values in the MyObject objects and then need to refresh the Charts to show the updated data.
I do know the data is processing fine. In my MyObject class, I'm logging the new values to Console in the setter for the property and see the new values show up.
You must do both operations (refresh and updating of control's parent object) from the main UI thread. If you are modifying a control from a background thread and not getting an exception that is bad luck because it is definitely an error.
The best way to do this is to use
theControl.Invoke(new MethodInvoker(MyUpdateMethod));
If you have a sample of how the update is done, we can give a better sample on how to properly call it from the background thread.
JaredPar is a pretty good answer. I would like to add to it a bit as to the reason your code sort of works.
With windows forms you can talk to the UI thread from other threads. This is really bad practice in all cases.
The catch is that when you do it, it is hard to catch because sometimes the UI will work as if nothing is wrong. item will get added or changed and the UI will reflect the changes. However other times running the same exact code, it will not work.
That is the catch with touching the UI from any thread other then the UI thread. The out come is inconsistent and that is why is very bad practice.
God I wish I could comment. :)
JaredPar's answer is good. It can cause problems in some instances (notably when the method is invoked before the form is finished being constructed). Here's a somewhat more robust implementation (using extension methods)
public static class ControlInvokeExtensions
{
public static void InvokeOnHostThread(Control host, MethodInvoker method)
{
if (IsHandleCreated)
Invoke(new EventHandler(delegate { method(); }));
else
method();
}
}
now you can call it this way
panel1.InvokeOnHostThread(() => panel1.Controls.AddRange(myArrayOfControls));
or if you're in the form:
InvokeOnHostThread(() => panel1.Controls.AddRange(myArrayOfControls));