Modal Progress Dialog - How to pass in delegate? - winforms

I have a basic form with a progress bar and want to pass in a delegate of sorts like this:
ProgressDialog.ShowAndExecute(delegate);
I can't figure out how to connect the delegate to progress messages.
void ShowAndExecute()
{
// Handle form disabling and whatnot...
thread = new Thread(new ThreadStart(ExecuteCommand));
while (thread.IsAlive)
{
Application.DoEvents();
Thread.Sleep(100);
}
}
// Example of the method I would like to pass in
void ExecuteCommand()
{
for (int i = 0; i < 10; i++) Thread.Sleep(1000);
}
I thought about creating an interface that the commands should implement. They can fire an event whenever an update occurs, but how do I let the calling thread know it was fired?
How do you handle passing in delegates that report progress through events and act (move progress bar) based on those? This is a static dialog (to make it easier to call throughout the app)

Only the ExecuteCommand knows its current progress. So some interface or class must be passed to the ExecuteCommand method. The ExecuteCommand will set the progress, then it's up to the implementation of the listener to determine what to do with the progress. In general, the listener will check if it's a significant change** in progress, and if so, it will BeginInvoke a call to update a progress bar.
** - If you process thousands of items in a short period of time, then you want to protect against calling BeginInvoke too much, otherwise it will lock up the UI thread.
void ExecuteCommand(IThreadController tc)
{
for (int i = 0; i < 10; i++) {
Thread.Sleep(1000);
tc.setProgress("processing ...", (i+1), 10);
}
}

Related

Get receive keyboard an mouse events from SDL2 in WPF application

I'm trying to capture mouse and keyboard events from SDL2 using the SDL2-CS binding library. The events are polled for but these events are never raised.
I think this is because the polling needs to happen on the UI thread. I tried initializing SDL from the UI thread by calling App.Current.Dispatcher.Invoke(Init) but no events are polled.
Basic implementation of my class:
public override void Initialize()
{
if (hooked)
{
return;
}
App.Current.Dispatcher.Invoke(Init); //Run on the UI thread
}
private void Init()
{
var init = SDL.SDL_Init(SDL.SDL_INIT_VIDEO);
if (init != 0)
{
throw new Exception("Could not initialize SDL");
}
hooked = true;
ListenForEvents();
}
private void ListenForEvents()
{
SDL.SDL_Event ev;
while (true)
{
if (SDL.SDL_PollEvent(out ev) != 1) //This is continuously trigged
{
continue;
}
switch (ev.type) //This is never reached
{
case SDL.SDL_EventType.SDL_MOUSEMOTION:
if (MouseMoved != null) { MouseMoved(this, ev.motion); }
break;
...
}
}
}
I'm wondring if I'm invoking the Init on the UI thread wrong, or if the SDL initialization is wrong.
P.S. Hooking with user32.dll is not desired because this code will run on non windows environments as well.
Looking at your code I would say your UI is blocked because ListenForEvents is not running on a different thread and invoking the Init call will run the method - that never returns - on the UI thread.
It might be a good idea to call Init invoked, but then you should start a new thread for polling.

Windows Form UI Update issue with Task in C#

We are working on a windows application which caters to an engineering calculation which are essentially very long running. So we are basically trying to keep the calculation module separate and working in a separate worker thread and pass it an Action delegate in method signature which will be invoked to report the calculation progress in the UI. The delegate handler declared in the UI will be updating the UI. We found that while a huge loop is running in the calculation, the UI is not showing the periodic progress and only displaying the final result. If a Thread Sleep for 1 millisecond is introduced in the calculation loop, the UI is getting updated correctly. This is not expected behavior as we are executing the calculation using a separate Task and updating the UI using BeginInvoke calls.
I have created a simple application to demonstrate our approach and code so that it is easier to understand. It is obvious that we are missing something very simple but cannot quite pin it down. Will appreciate any insights.
Thanks for reading.
private void cmdStart_Click(object sender, EventArgs e)
{
txtResultDIsplay.Text = "";
var maxIterations = long.Parse(txtIterationNo.Text.Trim());
var ui = TaskScheduler.FromCurrentSynchronizationContext();
Task<double> calculationTask = Task.Factory.StartNew<double>(
() => SumRootN(maxIterations, UpdateProgress));
var handleResultTask = calculationTask.ContinueWith((t) => DisplayResult(t),
CancellationToken.None, TaskContinuationOptions.OnlyOnRanToCompletion, ui);
}
private void DisplayResult(Task<double> calculationTask)
{
txtResultDIsplay.Text = "Final Calculation Result : " + calculationTask.Result.ToString();
}
private void UpdateProgress(string msg)
{
this.BeginInvoke((MethodInvoker)delegate
{
txtResultDIsplay.Text = msg;
});
}
public double SumRootN(long maxIterations, Action<string> progressUpdateDelegate)
{
int root = 20;
double result = 0;
for (long i = 1; i < maxIterations; i++)
{
Thread.Sleep(1);
result += Math.Exp(Math.Log(i) / root);
progressUpdateDelegate(result.ToString("0.00000"));
}
return result;
}
It is possible you are flooding the UI thread with your progress updates. You need to find a way to prevent lots of updates occurring.
We can solve the problem using tasks!
Task progressTask = null;
private void UpdateProgress(string msg)
{
//only schedule work if the task if not running
if(progressTask == null || progressTask.IsCompleted) //updates will end if there is an exception!
{
//Create a task representing the update
progressTask = Task.Factory.FromAsync<object>(BeginInvoke(new Action(() => txtResultDIsplay.Text = msg)), this.EndInvoke)
.ContinueWith(() => System.Threading.Thread.Sleep(100)); //add a sleep on the end
}
}
Note that locking will not do here as you want to skip the update if there is already an update occurring.

How to get Json Array to work inside Asyntask - Android [duplicate]

This question already has an answer here:
How to put Json inside asynctask - Android
(1 answer)
Closed 9 years ago.
I have an Asynctask that uses a Json function in the doInBackground part. The function collects an array of comments and places them into a variable called KEY_COMMENTS. In the onPreExecute it places the comments into a textView using a for loop to select each comment individually. The problem is that its not selecting each comment it will only select one. If I set the loop to go for more than 1 time it will crash the app. Here is my code,
class loadComments extends AsyncTask<JSONObject, String, JSONObject> {
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected void onProgressUpdate(String... values) {
super.onProgressUpdate(values);
}
protected JSONObject doInBackground(JSONObject... params) {
//do your work here
JSONObject json2 = CollectComments.collectComments(usernameforcomments, offsetNumber);
return json2;
}
#Override
protected void onPostExecute(JSONObject json2) {
try {
if (json2.getString(KEY_SUCCESS) != null) {
registerErrorMsg.setText("");
String res2 = json2.getString(KEY_SUCCESS);
if(Integer.parseInt(res2) == 1){
JSONArray array = json2.getJSONArray(KEY_COMMENT);
for(int i = 0; i < 2; i++) {
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.FILL_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
commentBox.setBackgroundResource(R.drawable.comment_box_bg);
layoutParams.setMargins(0, 10, 0, 10);
commentBox.setPadding(0,0,0,10);
commentBox.setOrientation(LinearLayout.VERTICAL);
linear.addView(commentBox, layoutParams);
commentBoxHeader.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));
commentBoxHeader.setBackgroundResource(R.drawable.comment_box_bg);
commentBoxHeader.setBackgroundResource(R.drawable.comment_box_header);
commentBox.addView(commentBoxHeader);
commentView.setText(array.getString(i));
LinearLayout.LayoutParams commentViewParams = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.FILL_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
commentViewParams.setMargins(20, 10, 20, 20);
commentView.setBackgroundResource(R.drawable.comment_bg);
commentView.setTextColor(getResources().getColor(R.color.black));
commentBox.addView(commentView, commentViewParams);
}
}//end if key is == 1
else{
// Error in registration
registerErrorMsg.setText(json2.getString(KEY_ERROR_MSG));
}//end else
}//end if
} //end try
catch (JSONException e) {
e.printStackTrace();
}//end catch
}
}
doInBackGround : method is used as a Thread !
onPostExecute : acts as a UI Thread !
So try to put your any-long running code in , doInBackGround method !
When an asynchronous task is executed, the task goes through 4 steps:
From the Docs :
onPreExecute(), invoked on the UI thread before the task is executed. This step is normally used to setup the task, for instance by showing a progress bar in the user interface.
doInBackground(Params...), invoked on the background thread immediately after onPreExecute() finishes executing. This step is used to perform background computation that can take a long time. The parameters of the asynchronous task are passed to this step. The result of the computation must be returned by this step and will be passed back to the last step. This step can also use publishProgress(Progress...) to publish one or more units of progress. These values are published on the UI thread, in the onProgressUpdate(Progress...) step.
onProgressUpdate(Progress...), invoked on the UI thread after a call to publishProgress(Progress...). The timing of the execution is undefined. This method is used to display any form of progress in the user interface while the background computation is still executing. For instance, it can be used to animate a progress bar or show logs in a text field.
onPostExecute(Result), invoked on the UI thread after the background computation finishes. The result of the background computation is passed to this step as a parameter.

Dispatching events into right thread

I have developed a wrapper for a library that uses a callback to notify events. This callback is called using another thread than UI's thread, so the wrapper uses the following script to call the event handlers into the right thread for a WinForm application.
void AoComm::Utiles::Managed::DispatchEvent( Delegate^ ev, Object^ sender, Object^ args )
{
ComponentModel::ISynchronizeInvoke^ si;
array<Delegate^>^ handlers;
if(ev != nullptr)
{
handlers= ev->GetInvocationList();
for(int i = 0; i < handlers->Length; ++i)
{
// target implements ISynchronizeInvoke?
si = dynamic_cast<ComponentModel::ISynchronizeInvoke^>(handlers[i]->Target);
try{
if(si != nullptr && si->InvokeRequired)
{
IAsyncResult^ res = si->BeginInvoke(handlers[i], gcnew array<Object^>{sender, args});
si->EndInvoke(res);
}else{
Delegate^ del = handlers[i];
del->Method->Invoke( del->Target, gcnew array<Object^>{sender, args} );
}
}catch(System::Reflection::TargetException^ e){
Exception^ innerException;
if (e->InnerException != nullptr)
{
innerException = e->InnerException;
}else{
innerException = e;
}
Threading::ThreadStart^ savestack = (Threading::ThreadStart^) Delegate::CreateDelegate(Threading::ThreadStart::typeid, innerException, "InternalPreserveStackTrace", false, false);
if(savestack != nullptr) savestack();
throw innerException;// -- now we can re-throw without trashing the stack
}
}
}
}
This code works pretty well, but I have read about Dispatcher class for WPF that do the same than my code (and more, of course).
So, is there something (class, mechanism, ...) equivalent to Dispatcher class for WinForms?
Thanks.
Right, this isn't the right way to do it. Winforms and WPF have different synchronization providers, they install theirs in System::Threading::SynchronizationContext::Current.
To use it, copy the Current value in your constructor. When you are ready to fire the event, check if it is nullptr. If it was then your object got constructed in a worker thread and you should fire your event directly. If it isn't then use the Post() method to run a helper method on the UI thread. Have that helper method fire the event.

C# Winforms: BeginInvoke still running on same thread?

I'm web developer and I'm trying to step into multithreading programming.
On one form I'm trying to run a method computing values in a second thread using asynchronous delegates.
I also want a progress bar showing actual progress in UI thread been notified.
delegate void ShowProgressDelegate(int total, int value);
delegate void ComputeDelegate(int value);
//Some method simulating sophisticated computing process
private void Compute(int value)
{
ShowProgress(value, 0);
for (int i = 0; i <= value; i++)
{
ShowProgress(value, i);
}
}
//Method returning values into UI thread
private void ShowProgress(int total, int value)
{
if (!this.InvokeRequired)
{
ComputeButton.Text = value.ToString();
ProgressBar.Maximum = total;
ProgressBar.Value = value;
}
else
{
ShowProgressDelegate showDel = new ShowProgressDelegate(ShowProgress);
this.BeginInvoke(showDel, new object[] { total, value });
}
}
//firing all process
private void ComputeButton_Click(object sender, EventArgs e)
{
ComputeButton.Text = "0";
ComputeDelegate compDel = new ComputeDelegate(Compute);
compDel.BeginInvoke(100000, null, null);
}
When I run this, everything is computing without any problem except it is still running in UI thread (I suppose so, because it freezes when I click some button on the form).
Why? I also attach buildable sample project (VS2010) with same code: http://osmera.com/windowsformsapplication1.zip
Thanks for helping neewbie.
In the code you've shown, you're doing nothing other than updating the progress bar - so there are thousands of UI messages to marshal, but nothing significant happening in the non-UI thread.
If you start simulating real work in Compute, you'll see it behave more reasonably, I suspect. You need to make sure you don't swamp the UI thread with progress updates like you are doing now.

Resources