Execute simple Logic without "locking" the UI - c#

I have a dialog with some TextBoxes and a button. When this button is clicked I want to change the button's Text, disable the button, start some Logic and then close the Window.
That logic has no return, it just sets a static variable. But it can take a minute because it connects to a DB.
How can I stop the WinForms UI from freezing? I'm looking for a simple approach, if possible no new Classes and Files.

Here's what I would do:
private async void ButtonClick()
{
//here, you're on the UI Thread
button1.Enabled = false;
await Task.Run(() => DoTheWork());
//you're back on the UI Thread
button1.Enabled = true;
}
private void DoTheWork()
{
//this will be executed on a different Thread
}

You could use a backgroundworker:
BackgroundWorker worker = new BackgroundWorker();
worker.WorkerSupportsCancellation = false;
worker.WorkerReportsProgress = false;
worker.DoWork += (s, e) =>
{
//perform action
};
worker.RunWorkerCompleted += (s, e) =>
{
//close window
};
worker.RunWorkerAsync();
See this for more details.

when button is clicked:
button.Enabled = false;
button.Text = "new text";
Task.Factory.StartNew(() =>
{
// do your tasks here and close the window.
// type your code normally like calling methods. setting variables etc...
StaticVariable = ExampleUsingMethod();
});
if your variable needs to be assigned to UI later then you need dispatcher.for example if you want to change button inside the new thread.
Task.Factory.StartNew(() =>
{
Dispatcher.Invoke(() =>
{
button.Enabled = false;
button.Text = "new text";
}
StaticVariable = ExampleUsingMethod();
});

You can have a thread that performs the calculation.
This may help: Threads in CSharp

Related

Change element properties in new thread

I am trying to implement a button; which changes its label to "Signing in...", keeps that label until an event gets completed, then it changes its label to old one again. But in this interval, it shouldn't freeze (basically UI thread will continue to execute). What kind of algorithm can be performed here? I just used this code but it's not working expectedly:
Task.Factory.StartNew(() =>
{
string old_label = sign_in_button.label.Content;
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Input,
new Action(() => {
sign_in_button.label.Content = "Signing in...";
}));
// Event gets executed here...
sign_in_button.label.Content = old_label;
});
You can use async await. Sample code below. You can also pass in your mainwindow dispatcher to your new thread, in case you are planning to update or fetch some UI data from there.
private async void Button_Click(object sender, RoutedEventArgs e)
{
var _dispatcher = App.Current.MainWindow.Dispatcher;
string old_label = sign_in_button.Content as string;
sign_in_button.Content = "Signing in...";
// Event gets executed here...
await Task.Run(() => signIn(_dispatcher));
sign_in_button.Content = old_label;
}
private void signIn(Dispatcher _dispatcher)
{
//THREAD DELAY TEST
Thread.Sleep(3500);
_dispatcher.BeginInvoke(() => { sign_in_button.Content = "Changed from another thread"; },DispatcherPriority.Send);
Thread.Sleep(3500);
}

"Cross thread operation not valid" Warning

In my c# form application i use BackgroundWorker but couldnt manage to change label1 field. Can anyone help me on that?
Thanks.
private void goButton_Click(object sender, EventArgs e)
{
_worker = new BackgroundWorker();
_worker.WorkerSupportsCancellation = true;
_worker.DoWork += new DoWorkEventHandler((state, args) =>
{
do
{
if (_worker.CancellationPending)
break;
setlabel();
} while (true);
});
_worker.RunWorkerAsync();
goButton.Enabled = false;
stopButton.Enabled = true;
}
private void setlabel()
{
label1.Text = "test";
}
You cannot manipulate controls that were created on the UI thread from a background thread. To accomplish that you can use the BeginInvoke method:
if (_worker.CancellationPending)
break;
this.BeginInvoke(new Action(setlabel));
The method:
Executes the specified delegate asynchronously on the thread that the control's underlying handle was created on.
You can't update UI elements not from UI thread (in your case within DoWork function).
You can use BackgroundWorker's ProgressChangedEventHandler to modify UI state within it's callback.

BackgroundWorker in ASP.NET page doesn't work as expected: not async and isBusy is always false

I'm pretty new on ASP.NET but I've already used BackgroundWorkers on desktop applications.
This time I've created a simple ASP page that shows a button. When it's clicked, a 5 second operation is called and I want to set a graphical "loading" state. For example, I simply want to disable that button.
I tried this way:
private BackgroundWorker worker = new BackgroundWorker();
// ...
protected void confirmButton_Click(object sender, EventArgs e)
{
if (selectedAccount == null || worker.IsBusy) return;
worker = new BackgroundWorker();
worker.WorkerReportsProgress = false;
worker.WorkerSupportsCancellation = false;
worker.DoWork += (ss, ee) => {
// the operation
sync.StartOperation(selectedAccount);
};
worker.RunWorkerCompleted += (ss, ee) =>
{
// Operation finished, update the GUI
Report finalReport = sync.GetFinalReport();
if (finalReport.HasErrors())
{
ShowErrorMessage("Error");
}
else
{
ShowSuccessMessage("Completed");
}
SetLoadingState(false);
};
// before starting the worker, set a "loading" status ( => disable the button)
SetLoadingState(true);
// then start!
worker.RunWorkerAsync();
}
There are two problems here:
The GUI is not updated to the "loading" state (aka button doesn't get disabled). Seems like the operation is not really "async"...
Since I can still press the button during the operation, I tried to rely on the worker.isBusy value... but this property is always false, even if the operation is clearly running.
For the record, the last part where RunWorkerCompleted should be called works. The GUI is correctly updated with error or success messages.
What's wrong with that? Thanks.

Disable Winforms button while OnClick code executes in background

I have some code in my "button_click" action. I want to disable the button during this code working.
private void button1_Click(object sender, RoutedEventArgs e)
{
button1.IsEnabled = false;
// some code (its take about 2-5 sec)
button1.IsEnabled = true;
}
But this doesn't work. The button never disables.
You need to run the "some code" part on a background thread:
button1.Enabled = false;
Task.Factory.StartNew(() => {
// some code (its take about 2-5 sec)
}).ContinueWith(task => {
button1.Enabled = true;
}, TaskScheduler.FromCurrentSynchronizationContext());
That is because your UI locks up during the entire action.
You should write the task in some sort of background thread.
You can use the BackgroundWorker for that, but better a Task.
BackgroundWorker bgw = new BackgroundWorker();
bgw.DoWork += bgw_DoWork;
bgw.RunWorkerCompleted += bgw_RunWorkerCompleted;
button.Enabled = false;
bgw.RunWorkerAsync();
private void bgw_DoWork(object sender, DoWorkEventArgs e)
{
// your task
}
private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// update the button
button.Enabled = true;
}
Dispatcher is responsible for message pumping in WPF. Every thread has dispatcher associated with it which is responsible for queuing stuffs on that thread based on DispatcherPriority of items.
In your case GUI rendering is done on DispatcherPriority.Render but right now dispatcher is busy executing your button click event handler code so GUI rendering never happens until it finishes with your code execution. That's why you see button to be refreshed only when your handler gets executed completely.
On a sidenote what McGarnagle proposed gonna work but you can do it other way round as well by explicitly queuing empty delegate on dispatcher with priority Render which will force all queued items with priority higher or equal to Render to be processed before proceeding further. Hence, you will see refresh on your GUI:
button1.IsEnabled = false;
Application.Current.Dispatcher.Invoke((Action)(() => { }),
DispatcherPriority.Render);
// some code (its take about 2-5 sec)
button1.IsEnabled = true;

Background thread in Window_Loaded event threading error

I have to load a window and in Window_Loaded I have to load some variables and show it on Window.
private void Window_Loaded_1(object sender, RoutedEventArgs e)
{
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += (o, ea) =>
{
try
{
//code to download some variables which will show on UI of Window Loading
}
catch (Exception ex)
{
//The calling thread cannot access this object because a different thread owns it.
}
};
worker.RunWorkerCompleted += (o, ea) =>
{
};
worker.RunWorkerAsync();
}
But I am getting a threading exception. Is there any way to show the variables value on window from DoWork of Backgroundworker?
You should retrieve the data you need in the DoWork section, then assign it to ea.Result, which will make it available in the RunWorkerCompleted section.
In the RunWorkerCompleted section, you can access ea.Result again, casting the object back to whatever type you assigned in DoWork, and apply the data as needed to your UI controls.
worker.DoWork += (o, ea) =>
{
ea.Result = GetMyData();
};
worker.RunWorkerCompleted += (o, ea) =>
{
var myData = (myDataType)ea.Result;
// Assign myData as needed to UI components...
};
You need to let Dispatcher schedule your code to execute on UI thread and marshal necessary parameters. Try something like this:
Dispatcher.Invoke(
new Action<string>(() =>
{
// Access UI from here
}),
DispatcherPriority.Normal
);
Although this (or something like this, since this is notepad code) will solve your problem, you should consider using MVVM pattern in your implementation. Then you will be able to make changes to ViewModel (just update data) and UI will update accordingly.

Categories

Resources