How would I update the UI in my Windows Phone app from a while, foreach, for, etc loop?
Have you considered using additional thread for this?
public MainPage()
{
InitializeComponent();
Thread thread = new Thread(() => ReadFile(/*params*/));
thread.Start();
}
private void ReadFile(/*params*/)
{
while(/*condition*/)
{
/* READ FILE */
//send task to UI thread to add object to list box
Dispatcher.BeginInvoke(() => listBox1.Items.Add("YOUR OBJECT"));
}
}
Long term action is happening in non-UI thread, what makes that UI thread doesn`t get frozen. In every loop iteration, non-UI Thread sends action via Dispatcher.BeginInvoke to UI thread to add new object to listbox.
Related
According to answer for this question Why Thread.Join() DOES NOT hang my application when called on UI thread? thread.Join should not hang UI if it called from STA thread. I used the same code as in linked question
private void button1_Click(object sender, EventArgs e)
{
string retValue = "";
Thread thread = new Thread(
() =>
{
retValue = LongRunningHeavyFunction();
});
thread.Start();
thread.Join();
button1.Text = retValue;
}
private string LongRunningHeavyFunction()
{
Thread.Sleep(5000);
return "Done";
}
Method Main in class Program marked as [STAThread]. But when I press button UI is freezed, I can't drag window etc. I'm confused. Am I missed something? Why UI is freezed in my case?
Thread.Sleep causes UI to freeze.
If you want to wait for a while in LongRunningHeavyFunction(), use a timer object.
Here is an example, how to use timer:
How to use a timer to wait?
thread.Join() tells the current thread to await thread. In this case, the current thread is the GUI thread, and you're telling it to await the worker thread. As a result, your GUI thread does nothing until the worker thread completes. And since the GUI thread is doing nothing, it isn't handling normal GUI activities, causing the freeze.
The solution is to not block your GUI. Instead, run your long-running process without awaiting it, such that your GUI thread can keep responding to the user. Then, once the long-running process does complete, use the GUI dispatcher to call back to set the result.
The code might look something like this:
private void button1_Click(object sender, EventArgs e)
{
Thread thread = new Thread(
() =>
{
// Perform work
var retValue = LongRunningHeavyFunction();
// Call the GUI thread
button1.Dispatcher.BeginInvoke(() =>
{
// .Dispatcher called the GUI thread.
// This code happens back in the GUI thread once the
// worker thread has completed.
button1.Text = retValue;
});
});
thread.Start();
}
private string LongRunningHeavyFunction()
{
Thread.Sleep(5000);
return "Done";
}
I have my VM implemented INotifyPropertyChanged interface. I created another thread T for populating a list that I bind to Xaml. After list is populated, I call PropertyChanged in thread T, and my UI got refreshed correctly.
My question is in what case I would need to use Dispatcher? Why I don't need to use Dispatcher in my case? I thought Dispatcher is used when the code in other thread want to notify the changes to the UI thread by enqueuing the changes to the UI refresh queue, such as adding items to ObservableCollection from another thread, and UI thread will then pull data from the queue.
private List<string> _ListData;
public List<String> ListData
{
get
{
if (_ListData == null)
Initialise( () => ListData = ReturnSlow());
return _ListData;
}
set { _ListData = value; }
}
private List<string> ReturnSlow()
{
List<string> Test = new List<string>();
Test.Add("1");
Test.Add("2");
Thread.Sleep(2000);
return Test;
}
public void Initialise(Action initialiser)
{
Task t = new Task(() =>
{
initialiser();
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("ListData"));
});
t.Start();
}
Your app has a main UI thread (usually ManagedThreadId==1). If you want to update the UI from an event that gets pull on some other thread you must use the dispatcher. A useful test here is the Dispatcher.CheckAccess() method that returns true if code is on UI thread and false if on some other thread. A typical call looks something like:
using System.Windows.Threading; // For Dispatcher.
if (Application.Current.Dispatcher.CheckAccess()) {
network_links.Add(new NetworkLinkVM(link, start_node, end_node));
}
else {
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(()=>{
network_links.Add(new NetworkLinkVM(link, start_node, end_node));
}));
}
If you're in the main window you can use:
Dispatcher.BeginInvoke(...
If you're in someother context eg a view model then use:
Application.Current.Dispatcher.BeginInvoke(
Invoke vs BeginInvoke
Use Invoke if you want the current thread to wait until the UI thread has processed the dispatch code or BeginInvoke if you want current thread to continue without waiting for operation to complete on UI thread.
MessageBox, Dispatchers and Invoke/BeginInvoke:
Dispatcher.Invoke will block your thread until the MessageBox is dismissed.
Dispatcher.BeginInvoke will allow your thread code to continue to execute while the UI thread will block on the MessageBox call until its dismissed.
CurrentDispatcher vs Current.Dispatcher!
Be ware of Dispatcher.CurrentDispatcher as my understanding of this is that is will return a Dispatcher for the current thread not the UI thread. Generally are you interested in the dispatcher on the UI thread - Application.Current.Dispatcher always returns this.
Additional note:
If you are finding you are having to check dispatcher CheckAccess often then a useful helper method is:
public void DispatchIfNecessary(Action action) {
if (!Dispatcher.CheckAccess())
Dispatcher.Invoke(action);
else
action.Invoke();
}
Which can be called as:
DispatchIfNecessary(() => {
network_links.Add(new NetworkLinkVM(link, start_node, end_node));
});
I want to show progress bar while my application create complex screen. my code is:
protected override void LoadSubject(object sender)
{
var win = new Spinner();
win.Show();
Thread th = new Thread(() =>
{
LoadSubjectImpl(sender);
win.Dispatcher.BeginInvoke(new Action(() => win.Close()));
});
th.Start();
}
private void LoadSubjectImpl(object sender)
{
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
StartServiceWorkflow(sender);
})
);
}
its works fine, but the progress bar freeze...
I must use the dispatcher in background thread because of "InvalidOperationException" and I think that the problem, but what can I do?
spinner = costum progressbar.
You will have no luck trying to achieve what you have described. If you'd read closely that InvalidOperationException you would know that you cannot manipulate UI from background thread. What you have done using Dispatcher is synchronizing StartServiceWorkflow to UI thread. So your code is executing in it - that causes the freeze.
To achieve some user experience enhancement you can delegate to background thread tasks like reading form database or processing data that is to be displayed.
I have faced with a problem of threads synchronization. My presenter analyzes some sensors and update UI form. I moved updating code into separate thread. It works fine, but if the user stops presenter when it is updating the view, the software freezes - I found that it happens when view.UpdateUI working (it just set some labels using Invoke). Where my problem is? I use compact framework 3.5 and Windows CE 5
using System.Threading;
class MyPresenter
{
UserControl view;
private Thread thread;
private ManualResetEvent cancelEvent;
public void Start()
{
cancelEvent = new ManualResetEvent(false);
thread = new Thread(UpdateView) { IsBackground = true };
thread.Start();
}
public void Stop()
{
if (thread != null) {
cancelEvent.Set();
thread.Join();
thread = null;
}
}
private void UpdateView()
{
while (cancelEvent.WaitOne(1000, false) == false) {
// analyze something
view.UpdateUI(...);
}
}
}
Don't update the UI thread directly from within a worker thread. Use a delegate instead.
For example: How to update the GUI from another thread in C#?
If your background thread is blocked calling your UI (via Control.Invoke), and then your UI thread is blocked calling your Stop method with its thread.Join() you've got yourself a classic fatal embrace. You should get rid of the Join and instead have the background thread raise one last event / notification when the Stop completes so the UI can deal with that (enable/disable buttons etc).
The whole point of the backgroundWorker is to update the UI after a time-consuming task. The component works as advertised in my WPF app.
However in my test, the callback is not invoked on the calling thread.
[Test]
public void TestCallbackIsInvokedOnClientThread()
{
var clientId = Thread.CurrentThread.ManagedThreadId;
int callbackThreadId = -1;
var manualEvent = new ManualResetEventSlim(false);
var someUIControl = new TextBox();
var bw = new BackgroundWorker();
bw.DoWork += (s,e) => e.Result = 5 ; // worker thread
bw.RunWorkerCompleted += (s, e) =>
{
try
{
callbackThreadId = Thread.CurrentThread.ManagedThreadId;
//someUIControl.Text = callbackThreadId.ToString();
manualEvent.Set();
}
catch (System.Exception ex)
{
Console.Out.WriteLine(ex.ToString());
}
};
bw.RunWorkerAsync();
if (!manualEvent.Wait(5000))
Assert.Fail("no callback");
Assert.AreEqual(clientId, callbackThreadId);
}
Result Message: Assert.AreEqual failed. Expected:<15>. Actual:<10>. callback not invoked on client Thread
What am I missing ?
In the Unit Test I see behavior like
------ Run test started ------
MainThread Id =21
Worker Thread Id =9
Callback Thread Id =9
In the Wpf App, this would be
MainThread Id =1
Worker Thread Id =14
Callback Thread Id =1
Update:
With Justin's answer, made the following changes and now the test passes
Before creating the BackgroundWorker
SynchronizationContext.SetSynchronizationContext(new DispatcherSynchronizationContext(control.Dispatcher));
Instead of using a event for signalling between the threads, simulate a message pump
.
for (int i = 0; i < 3; i++)
{
control.Dispatcher.Invoke(DispatcherPriority.Background,
new Action(delegate { }));
Thread.Sleep(50);
}
The behavior is different dues to the different contexts that you are running under.
When you call bw.RunWorkerAsync(), the SynchronizationContext is captured. This is used to dispatch out the RunWorkerCompleted call.
Under WPF it will use DispatcherSynchronizationContext which will marshall the completed call back to the UI thread. Under the test, this marshalling is unnecessary so it remains on the background worker thread.
I belive that the calling thread must support messagepumping (mean, being STA apartment and having an associated Dispatcher) so the background worker can post the callback. If it does not, the background worker has no option but execute the callback in its own thread. If you want to test it, see this link.
I ran into a problem in my code where the user closing a window caused a save, that in turn used a BackgroundWorker to update the home window and it did not run the RunWorkerCompleted because the thread that started the BackgroundWorker had terminated when the window closed.
I had to change the closing window's save run in the home window's context so that after the BackgroundWorker completed, it had a thread to return to.
In my case I am using Windows Forms and controls don't have a Dispatcher property (see the answer in no definition for dispatcher).
Gishu's solution works as well if we use Dispatcher.CurrentDispatcher instead of the one in the control.
On test initialisation:
// I am using a field Dispatcher _dispatcher
_dispatcher = Dispatcher.CurrentDispatcher;
And then when waiting for the background task to be completed:
_dispatcher.Invoke(DispatcherPriority.Background, new Action(delegate { }));
Thread.Sleep(50);