I have got this code. It works but it freezes the UI.
What I want to know is how to use WPF BeginInvok method corectly?
private void ValidateAuthURL_Click(object sender, RoutedEventArgs e)
{
((Button)sender).Dispatcher.BeginInvoke(DispatcherPriority.Input,
new ThreadStart(() =>
{
bool result = false;
try
{
Your delegate is going to be executed in the UI thread. That's what Dispatcher.BeginInvoke is there for. I assume you really want to execute that delegate in a background thread... then you should use Dispatcher.BeginInvoke to get back to the UI thread in order to update the UI later.
In terms of getting to a background thread, you could:
Use the thread pool directly (ThreadPool.QueueUserWorkItem)
Use BackgroundWorker
Start a new thread
Use Task.Factory.StartNew (if you're using .NET 4)
Related
What I'm trying to do is perform a heavy task triggered by a button event on the MainWindow, but still be able to drag the window freely. I've tried both the async/await pattern and creating new threads. However, threads will be nonblocking, MainWindow still freezes. Here's the code:
uiTIN.Click += async (o, e) =>
{
var _ = await Task.Run(() => job());
};
That's the button callback and here is the func:
private int job()
{
this.Dispatcher.Invoke(() =>
{
//Other function calls here omitted
});
return 0;
}
EDIT: The workaround was to use BackgroundWorker and I have also decorated dependent UI code snippets in Dispatcher Invoke function
From Microsoft's doccumentation on Dispatcher (emphasis mine):
In WPF, a DispatcherObject can only be accessed by the Dispatcher it is associated with. For example, a background thread cannot update the contents of a Button that is associated with the Dispatcher on the UI thread. In order for the background thread to access the Content property of the Button, the background thread must delegate the work to the Dispatcher associated with the UI thread. This is accomplished by using either Invoke or BeginInvoke. Invoke is synchronous and BeginInvoke is asynchronous. The operation is added to the queue of the Dispatcher at the specified DispatcherPriority.
So basically what you're doing is call an asynchronous method, and then forcing it to run on the UI thread, which accomplishes nothing.
In your //Other function calls here omitted, I'm asuming that you need to access some part of the UI, if that's not the case, all you have to do is remove the Dispatcher.Invoke from your method.
If my assumptions are right, then you must figure out a way of splitting your function, so that the part that isn't UI related run in a Background thread and only what needs to run on the UI Thread actually do.
My suggestion is to use a Background Worker. Here's how it'd look:
uiTIN.Click += (o, e) =>
{
job();
};
... and then ...
private int job()
{
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += (s, e) =>
{
// Part of other function calls here omitted that don't need to run on the UI thread
Dispatcher.Invoke(() =>
{
// Part of other function calls here omitted that must run on the UI thread
});
};
worker.RunWorkerAsync();
return 0;
}
The normal practice is that you have to return from buttons onClick event callback as soon as you can in order to avoid blocking the main thread(or some refer to UI thread). If the main thread is blocked the application will look like frozen. This is a fundamental design of any GUI application to synchronize UI flow.
You start an async task in callback but you also wait for the task to finish before returning. You should start a BackgroundWorker in the onClick event then return.
It has been explained quite well already why your code was blocking the UI thread (queuing your work on the Dispatcher). But I would not recommend the usage of the BackgroundWorker, I would rather fix your code with Task.Run for several reasons all explained in this article: https://blog.stephencleary.com/2013/09/taskrun-vs-backgroundworker-conclusion.html
I have a function which I need to run in background because it freezes the UI until it completes. I tried to use Async/Await which lets me use the UI no matter the function completes running or not, but I noticed it is much slower. Why using async/await to a function takes longer time then calling that same function directly ? Is there any other alternative ?
private void btnClick(object sender, EventArgs e)
{
Math4OfficeRibbon.CallFunction();
MessageBox.Show("Task Finished");
}
public async void CallFunction()
{
await Task.Run(() => AwaitedFunction());
}
public static void AwaitedFunction()
{
// Do Something
// Takes longer time this way
}
In order to find out why it's much slower you can track events down in visual studio by using Console.WriteLine($"{event name} {DateTime.Now}")
And then seeing where it takes the most time in output window.
However about the alternatives, I suggest you use BackgroundWorker to run your tasks.
note that you need to invoke controls in order to make changes to the ui through the backgroundWorker
BackgroundWorker _worker = new BackgroundWorker();
_worker.DoWork+=(o,args)=>
{
//your code here.
}
_worker.RunWorkerAsync();
You also have RunWorkerCompleted event which you can use to do things after your task is done running.
Backgroundworker also has the IsBusy property which you can use along with a while loop to keep the thread waiting for its completion without freezing the UI by doing :
While(_worker.IsBusy)
{
Application.DoEvents();
}
In order to invoke to do things on the ui thread you need to do the following within BackgroundWorker:
BeginInvoke(new Action(()=>
{
//ui action here for example:
MessageBox.show("test")
}));
However in order to find out why your asynchronous operation takes alot of time you have to trace it using the console because you have all the code and you know what you're doing.
What I want to do:
I'm using a web service DLL in WPF(c#). The DLL contains a web service that you can see it in my code as SmsSender class. Calling each method of this class is time-consuming so I need run its methods in other threads.
What I do:
I set DataView object (that is "returned value" from method) to ItemsSource of DataGrid. So I use Dispatcher.BeginInvoke().
My problem:
my problem is using Dispatcher.BeginInvoke() can freeze my program even I run it in a different thread. I want to call method without freezing. Is it possible to define a time-out?
Update1:
How can I set DataView from time-consuming method to DataGrid?
My code:
Action action = () =>
{
SmsSender sms = new SmsSender();
dgUser1.ItemsSource = sms.GetAllInboxMessagesDataSet().Tables[0].DefaultView;
};
dgUser1.Dispatcher.BeginInvoke(action);
Thanks in advance
Thats easy. You should NEVER do blocking I/O on your UI thread.
SmsSender sms = new SmsSender();
DataView result = sms.GetAllInboxMessagesDataSet().Tables[0].DefaultView
Action action = () =>
{
dgUser1.ItemsSource = result;
};
dgUser1.Dispatcher.BeginInvoke(action);
However this is actually NOT how you should write WPF apps. This pattern is done using WinForms like patterns. You should REALLY be using DataBinding.
Databinding would take care of all your Dispatcher calls.
Dispatcher.BeginInvoke runs delegate on UI thread and since you have put complete action on dispatcher, it will be executed on UI thread which results in UI hang issue.
There are many ways to delegate time consuming operation on to background thread and dispatch UI calls on UI thread.
Simple example is to use BackgroundWorker. Put non-UI stuff in DoWork event handler and UI operation on RunWorkerCompleted handler which is called on UI thread only so need to dispatch calls to UI disptacher.
Small sample -
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork+=new DoWorkEventHandler(worker_DoWork);
worker.RunWorkerCompleted +=
new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
worker.RunWorkerAsync();
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
dgUser1.ItemsSource = (DataView)e.Result;
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
SmsSender sms = new SmsSender();
e.Result = sms.GetAllInboxMessagesDataSet().Tables[0].DefaultView;
}
I'm going crazy with a simple code in which I use a BackgroundWorker to automate the basic operations. Should I add a content to the clipboard.
After executing this code in the method of the BackgroundWorker:
Clipboard.SetText (splitpermutation [i]);
I get an error that explains the thread must be STA, but I do not understand how to do.
Here more code: (not all)
private readonly BackgroundWorker worker = new BackgroundWorker();
private void btnAvvia_Click(object sender, RoutedEventArgs e)
{
count = lstview.Items.Count;
startY = Convert.ToInt32(txtY.Text);
startX = Convert.ToInt32(txtX.Text);
finalY = Convert.ToInt32(txtFinalPositionY.Text);
finalX = Convert.ToInt32(txtFinalPositionX.Text);
incremento = Convert.ToInt32(txtIncremento.Text);
pausa = Convert.ToInt32(txtPausa.Text);
worker.WorkerSupportsCancellation = true;
worker.RunWorkerAsync();
[...]
}
private void WorkFunction(object sender, DoWorkEventArgs e)
{
[...]
if (worker.CancellationPending)
{
e.Cancel = true;
break;
}
else
{
[...]
Clipboard.SetText(splitpermutation[i]);
[...]
}
}
You could marshal this to the UI thread to make it work:
else
{
[...]
this.Dispatcher.BeginInvoke(new Action(() => Clipboard.SetText(splitpermutation[i])));
[...]
}
The BackgroundWorker runs on the .NET thread pool. Thread pool threads run in the COM multi-threaded apartment. To use the clipboard, you must be running in a single-threaded apartment. You could create your own thread and set it to run in an STA, but it would probably be best to use Control.Invoke (or BeginInvoke) to get back onto a user-interface thread (which must be an STA thread).
The exception you're getting is because you're trying to do something on the UI thread from outside the UI thread (a BackgroundWorker, as the name implies, does something in the background, and for that it needs to create a separate thread).
While the answer posted by Reed (that is, by using Dispatcher.BeginInvoke) is one way to do avoid this exception, I'm wondering WHY you are trying to send something to the clipboard from the main work method in the first place...
The BackgroundWorker exposes events like ProgressChanged (which you can call periodically from your work method) or RunWorkerCompleted (which will fire when the main work method finishes).
Using Clipboard.SetText in either of these events should not cause the exception you're seeing, and this seems to be the preferable way of doing things on the UI thread when working with the BackgroundWorker.
In my wpf application I have added a piece of code in the button click as below:
private void btn_convert_Click(object sender, RoutedEventArgs e)
{
Thread t = new Thread(new ThreadStart(WorkerMethod));
t.SetApartmentState(ApartmentState.MTA);
t.IsBackground = true;
t.Start();
}
Inside my WorkerMethod() method I have some code like the line below:
btn_convert.Content = "Convert";
When it reaches to this line it throws the exception as the calling thread cannot access this object because a different thread owns it.
I dont want to use Dispatcher as it freezes the UI.. UI should be responsive so I have not opted for Dispatcher invoke or BeginInvoke.
Please give me your valuable suggestions.
I dont want to use Dispatcher as it freezes the UI.. UI should be responsive so i am not opted for Dispatcher invoke or BrginInvoke.
That just shows that you've used the dispatcher badly.
You must access the UI from the UI thread. That doesn't mean your whole WorkerMethod needs to run on the UI thread, but this line:
btn_convert.Content = "Convert";
definitely does. So you might want to keep your current code for starting a thread (do you really need to set the apartment state though) but change any code accessing the UI to use the dispatcher. For example:
Action setButtonContentAction = () => btn_convert.Content = "Convert";
Dispatcher.BeginInvoke(setButtonContentAction);
Alternatively, depending on what your WorkerThread is doing - and if you're using C# 5 - you might want to use the new async features. That can make it easier to keep UI work on the UI thread, but it does depend on what else is going on.
UI changes can only be applied by the main thread. You can check if the main thread call is necessary:
if (btn_convert.InvokeRequired)
{
btn_convert.Invoke((MethodInvoker)(() => btn_convert.Content = "Convert"));
}