Updating UI Thread immediately - c#

I'm trying to enable a busy indicator on log in. The problem I'm having is it won't enable until everything is done executing. How can I immediately tell the thread to update the UI as soon as I log in to start the indicator asap?
private void LoginButton_Click(object sender, RoutedEventArgs e)
{
this.Dispatcher.Invoke((Action)(() =>
{
radBusyIndicator.IsBusy = true;
//var backgroundWorker = new System.ComponentModel.BackgroundWorker();
//backgroundWorker.DoWork += new System.ComponentModel.DoWorkEventHandler(backgroundWorker_DoWork);
//backgroundWorker.RunWorkerAsync();
}));
string error = string.Empty;
long userId = 0;
//Login code here....
//........... bunch of other code. etc..
}

The UI will update as soon as the UI thread is free. There is no need for Dispatcher.Invoke in this case, as you're already in the UI thread.
The key here is to move the "work" into a background thread, ie:
private void LoginButton_Click(object sender, RoutedEventArgs e)
{
radBusyIndicator.IsBusy = true;
LoginButton.IsEnabled = false; // Prevent clicking twice
string error = string.Empty;
long userId = 0;
// Start this in the background
var task = Task.Factory.StartNew(()=>
{
//Login code here....
//........... bunch of other code. etc..
});
// Run, on the UI thread, cleanup code afterwards
task.ContinueWith(t =>
{
// TODO: Handle exceptions by checking t.Exception or similar...
radBusyIndicator.IsBusy = false;
LoginButton.IsEnabled = true;
}, TaskScheduler.FromCurrentSynchronizationContext());
}
If you're using C# 5, you can simplify this by making your login and other code asynchronous:
private async void LoginButton_Click(object sender, RoutedEventArgs e)
{
radBusyIndicator.IsBusy = true;
LoginButton.IsEnabled = false; // Prevent clicking twice
long userId = 0;
// Call async method with await, etc...
string error = await DoLoginAsync(userId);
var result = await BunchOfOtherCodeAsync();
radBusyIndicator.IsBusy = false;
LoginButton.IsEnabled = true;
}

You can use BAckground Worker Thread and subsribe its two eventHandlers to your events which you want to work on..
for eg-
BackgroundWorker Worker=new BackgroundWorker();
worker.DoWork+=Yorevent which will do the timeTaking Task();
Worker.RunWorkerCompleted+=YOurEvent which will Update your UI after the work is done();
worker.RunWorkerAsync();
this way it will not cause any thread Error too..
Just Enable your BusyIndicator as Your TimeTaking TAsk start and when the timeTaking Task is done just Disable your Busy Indicator in RUnWorkerCompleted Event.

Related

How to disable a button after click for only 3 seconds in a WinUI 3 Application?

I have a button that I want to disable for 3 seconds so that it's not abused. I wanted to add a Timer(3000); inside the Click event however the example code I found is using outdated method and is not working. I tried another code (which can be found below) however this throws System.Runtime.InteropServices.COMException: 'The application called an interface that was marshalled for a different thread. (0x8001010E (RPC_E_WRONG_THREAD))' error.
private void CodeButton_Click(object sender, RoutedEventArgs e)
{
CodeButton.IsEnabled = false;
var timer = new Timer(3000);
timer.Elapsed += (timer_s, timer_e) =>
{
CodeButton.IsEnabled = true;
timer.Dispose();
};
timer.Start();
Launcher.LaunchUriAsync(new Uri("https://www.hoppie.nl/acars/system/register.html"));
}
You need to use the main thread (the thread that instantiated UI components) to update UI. You get that error because the timer will work with another thread, not the main thread.
You can do it this way:
private async void Button_Click(object sender, RoutedEventArgs e)
{
try
{
// You can update the UI because
// the Click event will use the main thread.
this.Button.IsEnabled = false;
List<Task> tasks = new();
// The main thread will be released here until
// LaunchUriAsync returns.
tasks.Add(Launcher.LaunchUriAsync(new Uri("https://www.hoppie.nl/acars/system/register.html")));
tasks.Add(Task.Delay(3000));
await Task.WhenAll(tasks);
// The main thread will be back here.
}
finally
{
// This will enable the button even if you face exceptions.
this.Button.IsEnabled = true;
}
}

How to avoid UI blocking when starting another thread in C# WPF?

I'm trying to execute a thread without blocking UI , I've used this code but when I execute my application , it won't execute the thread and nothing is shown after clicking on DoButton event
public void DoThread()
{
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += MyFunctionDoThread;
var frame = new DispatcherFrame();
worker.RunWorkerCompleted += (sender, args) => {
frame.Continue = false;
};
worker.RunWorkerAsync();
Dispatcher.PushFrame(frame);
}
private void Dobutton_Click(object sender, RoutedEventArgs e)
{
DoThread(); // Process will be executed
}
public void MyFunctionDoThread()
{
// Some Tasks
ProcessStartInfo startInfo = new ProcessStartInfo();
Process.Start(startInfo);
// ...
}
How I can perform a task ( thread ) without blocking the UI?
You should really use Task/async/await for any background work. BackgroundWorker is rather old.
public async void Dobutton_Click(object sender, RoutedEventArgs e)
{
try{
var result = await Task.Run(MyFunctionDoThread);
// Update the UI, or otherwise deal with the result
}
catch{
// deal with failures, like showing a dialog to the user
}
}
how can I use it , the await require to return task action
await requires the method to be marked with async, it does not require the method to return a task. It is a guideline to return a task, so that the caller can deal with any failures. But for things like button event handlers you are at the end of the line, there is no one else to deal with any failure, so you should instead make sure you do it yourself with a try/catch.

wpf c# background worker works when execution finished

I have a wpf application where I want to start a loading indicator before a task and end after task done. But the indicator starts after the task executes.
What I am trying is as follows.
private void RunAllScriptsChildwdwBtnOK_Click(object sender, RoutedEventArgs e)
{
worker.RunWorkerAsync(); // this supposed to start progress bar
_RunAllScripts_Click();
}
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
this.Dispatcher.Invoke(() =>
{
... Start loading indicator
});
}
private void worker_RunWorkerCompleted(object sender,
RunWorkerCompletedEventArgs e)
{
... End loading indicator
}
But loading indicator starts and ends (as supposed in worker events) only after
_RunAllScripts_Click(); method execution is complete.
(I found that after unsubscribing from worker_RunWorkerCompleted event, progress bar starts and stays as is because no code to end it).
Also I want to add that, breakpoint hits worker_DoWork method before the execution, but UI updates after execution as I indicated above.
Thanks for all help you will be able to provide.
If i was you i would use the async + await keyword for this
private async void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
// this is where you would enable your indicator
Button.IsEnabled = false;
await Task.Run(
() =>
{
// this is where you put your work, which should be executed in the background thread.
Thread.Sleep(2000);
});
// this is where you would disable it
Button.IsEnabled = true;
}
Using async/await will work. The await keyword will allow you to run work without affecting/blocking the UI thread (allowing message pumping to still occur). Once the work has finished, any code after the await keyword will execute.
Note that I have also wrapped the await work in an InvokeAsync call, as it appears that additional work you are doing required UI thread access.
private async void RunAllScriptsChildwdwBtnOK_Click(object sender, RoutedEventArgs e)
{
//TODO ... Start loading indicator
await Task.Run(async ()=>
{
await Application.Current.Dispatcher.InvokeAsync(()=>
{
_RunAllScripts_Click();
});
});
//TODO ... End loading indicator
}
Dear kind people helping me about this subject, thank you all.
This works for me, hope it works for all.
BackgroundWorker bwTestAll = new BackgroundWorker() { WorkerReportsProgress = true };
bwTestAll.DoWork += new DoWorkEventHandler(TestAll);
bwTestAll.RunWorkerCompleted += TestAll_RunWorkerCompleted;
//this is where I initialize my loading ring and other stuff and marshall background
//worker to do the main work
Dispatcher.Invoke(new Action(() =>
{
EnableLoading = true;
RunAllScriptsTest.IsEnabled = false;
}), DispatcherPriority.ContextIdle);
bwTestAll.RunWorkerAsync();
//this is my main work
void TestAll(object sender, DoWorkEventArgs e)
{
presenter.RunAllScripts(true);
}
//this is where I do my post-work stuff
private void TestAll_RunWorkerCompleted(object sender,
RunWorkerCompletedEventArgs e)
{
Dispatcher.Invoke(new Action(() =>
{
/
EnableLoading = false;
RunAllScriptsTest.IsEnabled = true;
DbExecGrid = this.ExecutionResults;
ShowOrHideExecGrid(this.EnableOrDisableGrid);
}), DispatcherPriority.ContextIdle);
}
*Please Notice that Dispatcher with "DispatcherPriority.ContextIdle" works for me.

c# Thread which creates image getting cancelled

I have a event handler attached to the selectionChanged event on a DataGridView. In this handler I need to create and load an image and then display it in a picture box. The trouble I'm having is, if I jump between row selections quickly the application seems to hang, which is the issue I was trying to avoid.
Here is my code:
private void loadJobSheet(Job currentJob)
{
if (this.jobCardImageThread != null && this.jobCardImageThread.IsAlive)
this.jobCardImageThread.Abort();
Image jobCardImage = null;
this.jobCardImageThread = new Thread(new ThreadStart(
delegate()
{
SavedDocument document = currentJob.SavedDocument;
DocumentConverter<Bitmap> converter = DocumentConverterFactory<Bitmap>.getDocumentConverterForType(Path.GetExtension(document.Document_Name).Replace('.', ' ').Trim().ToUpper(), typeof(Bitmap));
jobCardImage = (Image)converter.convertDocument(FileUtils.createTempFile(document.Document_DocumentData.ToArray(), document.Document_Name));
}
));
jobCardImageThread.Start();
this.picLoadingJobCard.Visible = true;
jobCardImageThread.Join();
if (jobCardImage != null)
{
this.picJobCard.Image = jobCardImage;
this.picLoadingJobCard.Visible = false;
}
}
You are waiting for the separate thread to finish when you do
jobCardImageThread.Join();
This blocks the UI thread, which suspends the application.
You should remove the Join() call, create a separate method out of anything after the Join() call, and call that method from the delegate. Probably use an Invoke(...) call to switch back to the UI thread.
I think your problem is jobCardImageThread.Join(); With this statement you tell your Thread to wait for the other to finish. This way your UI is hanging.
Why don't you use a background-worker. For example:
Put this into your constructor
this.backgroundWorker = new BackgroundWorker();
this.backgroundWorker.DoWork += new DoWorkEventHandler(backgroundWorker_DoWork);
this.backgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker_RunWorkerCompleted);
this.backgroundWorker.WorkerSupportsCancellation = true;
And add the following methods:
private BackgroundWorker backgroundWorker;
private AutoResetEvent resetEvent = new AutoResetEvent(false);
private Thread thread;
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
this.picLoadingJobCard.Visible = true;
Job currentJob = (Job)e.Argument;
SavedDocument document = currentJob.SavedDocument;
DocumentConverter<Bitmap> converter = DocumentConverterFactory<Bitmap>.getDocumentConverterForType(Path.GetExtension(document.Document_Name).Replace('.', ' ').Trim().ToUpper(), typeof(Bitmap));
Image jobCardImage = (Image)converter.convertDocument(FileUtils.createTempFile(document.Document_DocumentData.ToArray(), document.Document_Name));
e.Result = jobCardImage;
}
private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
{
//error-handling
}
else if (e.Cancelled)
{
//cancel-handling
}
else
{
Image jobCardImage = e.Result as Image;
if (jobCardImage != null)
this.picJobCard.Image = jobCardImage;
}
this.picLoadingJobCard.Visible = false;
this.resetEvent.Set();
}
private void loadJobSheet(Job currentJob)
{
if (this.thread != null)
this.thread.Abort();
this.thread = new Thread(new ThreadStart(
delegate()
{
if (this.backgroundWorker.IsBusy)
{
this.backgroundWorker.CancelAsync();
this.resetEvent.WaitOne();
}
this.backgroundWorker.RunWorkerAsync(currentJob);
}));
this.thread.Start();
}
If you create a background Thread and immediately call Join after running it, you basically just wasted time and memory for creating a synchronous method, because your current thread will block until the background thread is finished. If the current thread is a UI thread, this will be pretty obvious.
Also, using Thread.Abort to kill a thread is not recommended.
I would suggest creating a long-lived background thread which will most of the time wait for a signal from the main thread. This will ensure that you don't unnecessarily create multiple threads in case you are receiving more requests than your worker method can handle.
This is the general idea:
// have a long lived and prosperous thread which handles jobs
private readonly Thread _backgroundWorker;
// you need a way to signal the thread to continue running
private readonly AutoResetEvent _signalNewTask;
// you need a flag indicating you want to stop (to avoid aborting the thread)
private volatile bool _keepRunning;
// and you need to pass the current job to that thread
private volatile Job _currentJob;
The loop should look something like this:
// this runs on a background thread
private void WorkerLoop()
{
Job lastJob = null; Image lastResult = null;
while (_keepRunning)
{
// use an AutoResetEvent for cross-thread signalization
_signalNewTask.WaitOne();
// make sure the app isn't ending
if (!_keepRunning)
break;
// don't bother if we already processed this job
if (lastJob == _currentJob)
continue;
// capture the job in a local variable
lastJob = _currentJob;
// long processing
lastResult = LoadImage(lastJob);
// check if this is still the last requested job
if (_keepRunning && lastJob == _currentJob)
DisplayImage(lastResult);
}
}
To schedule a job for execution, you simply set the field and signal the event:
private void ScheduleNewJob(Job nextJob)
{
// don't waste time if not needed
if (nextJob == _currentJob)
return;
_picLoadingJobCard.Visible = true;
_currentJob = nextJob;
_signalNewTask.Set();
}
You'll also need to add initialization and cleanup code to your Form:
public SomeForm()
{
InitializeComponent();
_keepRunning = true;
_signalNewTask = new AutoResetEvent(false);
_backgroundWorker = new Thread(WorkerLoop);
_backgroundWorker.IsBackground = true;
_backgroundWorker.Priority = ThreadPriority.BelowNormal;
_backgroundWorker.Start();
}
protected override void OnFormClosed(FormClosedEventArgs e)
{
// set the running flag to false and signal the thread
// to wake it up
_keepRunning = false;
_signalNewTask.Set();
// this will lock very shortly because the background
// thread breaks when the flag is set
_backgroundWorker.Join();
base.OnFormClosed(e);
}
And since DisplayImage (or whatever) will be called from a background thread, you have to schedule on the UI thread by calling Invoke:
private void DisplayImage(Image result)
{
if (this.InvokeRequired)
{
Invoke(new Action<Image>(DisplayImage), result);
return;
}
_picLoadingJobCard.Visible = false;
_picJobCard.Image = result;
}

C# - How to work with background worker with custom code -Run,Pause,Stop?

I am working with Background Worker but neither i am able to synchronize my progress bar nor able to stop or abort the process.
in my dowork function
void bw_DoWork(object sender, DoWorkEventArgs e)
{
if(bw.CancellationPending==true)
{
e.cancel=true;
return;
}
else
{
e.Result = abc();
}
}
int abc()
{
//my work
Count++;
return count;
}
void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if(bw.CancellationPending==true)
{
button17.Visibility = Visibility.Visible;
label1.Content = "Aborted";
}
button17.Visibility = Visibility.Visible;
label1.Content = "Completed";
}
private void Cancel_Click(object sender, RoutedEventArgs e)
{
if(bw.IsBusy)
{
bw.CancelAsync();
}
}
Now i want to know how could i Synchronize my Progress Bar and how to exit from the process?
Have you set the BackgroundWorker.WorkerReportsProgress && BackgroundWorker.WorkerSupportsCancellation properties on your instance to be true?
e.g.
var myBackgroundWorker = new BackgroundWorker();
myBackgroundWorker.WorkerReportsProgress = true;
myBackgroundWorker.WorkerSupportsCancellation = true;
//the rest of the init
If you want to report progress, you need to call the BackgroundWorker.ReportProgress() method from inside your DoWork.
This is a rubbish and trivial answer but give the Task Parallel library a whirl.
http://msdn.microsoft.com/en-us/library/dd537608.aspx
This library encapsulates threads as discrete Task objects. It supports cancellation.
Be warned that in a worker thread, pause and cancellation operation have to be supported by the worker code itself, by polling pause/cancel flags and tokens. You cannot safely achieve these operations with threads alone.
It is a nicer pattern to work with
As for your question, 2 flags are required to support your operations. You will be need to check them at intervals during the worker code.
bool pause = false;
bool cancel = false;
void DoWork()
{
try
{
...
//periodically check the flags
if(cancel) return;
while(paused){}; //spin on pause
...
}
finally
{
//cleanup operation
}
}
Alastair Pitts' answer illustrates how background worker supports these features.
So does MSDN ;) http://msdn.microsoft.com/en-us/library/cc221403%28v=vs.95%29.aspx
(You might want to check out this other SO question/answer for details about the new async facility! It greatly improves the quality of life of developing this kind of operations!)
BackgroundWorker is event-based, basic usage is the following (the link provides many useful additional details):
var worker = new BackgroundWorker();
// The following two props must be true:
// #1: The worker will be enabled to signal its progress
worker.WorkerReportsProgress = true;
// #2: The worker will accept cancellation
worker.WorkerSupportsCancellation = true;
// Now the events:
worker.DoWork += (s,e) =>
{
int i = 0; // This goes from 0 to 100
// Do code, update 'i'
worker.ReportProgress(i);
worker.CancelAsync(); //... to cancel the worker if needed
// WARNING: This code *cannot* interact with the UI because
// it's running in a different thread
};
worker.ProgressChanged += (s,e)=>
{
// This is executed when you call ReportProgress() from DoWork() handler
// IMPORTANT: All UI interaction **must** happen here
// e.ProgressPercentage gives you the value of the parameter you passed to
// ReportProgress() (this mechanism is a perfect fit for a progress bar!)
};
worker.RunWorkerCompleted+= (s,e) =>
{
// code here runs when DoWork() is done, is canceled or throws.
// To check what happened, the link provides this sample code:
if (e.Cancelled == true)
{
// Cancelled!
}
else if (e.Error != null)
{
// Exception !
}
else
{
// Work completed!
}
};
worker.RunWorkerAsync();
It's important to know that (extracted from the link above):
You must be careful not to manipulate any user-interface objects in your DoWork event handler. Instead, communicate to the user interface through the ProgressChanged and RunWorkerCompleted events.
UPDATE Lambdas here are used to keep code compact. You can obviously use "normal" handlers or whatever other method of attaching code to events you like/want/need.

Categories