Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Updating the UI with Xamarin

I am having difficulty updating the UI using Xamarin. The objective is to make reactive UI so the user knows that application is thinking. Below are my attempts.

Attempt 1

private void BeginProcess(string fileName)
{
     Device.BeginInvokeOnMainThread(() => {
         iContent.Text = "UI Successfully updated.";
     });

     Device.BeginInvokeOnMainThread(() => {
         ProccessFolder testMethod = new ProccessFolder.Initialize(fileName);
     });
}

Attempt 2

private void UpdateUI () {
    iContent.Text = "UI Successfully updated.";
}

private void BeginProcess(string fileName)
{
     System.Threading.Thread t = new System.Threading.Thread(UpdateUI);
     t.Priority = System.Threading.ThreadPriority.Highest;
     t.IsBackground = false;
     t.Start();

     Device.BeginInvokeOnMainThread(() => {
         ProccessFolder testMethod = new ProccessFolder.Initialize(fileName);
     });
}

Attempt 3

private void BeginProcess(string fileName)
{
     Device.BeginInvokeOnMainThread(() => {
         iContent.Text = "UI Successfully updated.";
     });

     Task.Delay(5000);

     Device.BeginInvokeOnMainThread(() => {
         ProccessFolder testMethod = new ProccessFolder.Initialize(fileName);
     });
}

Unfortunately none of these work. What does work is if I place the ProcessFolder method in a background thread and invoke the UI changes on the main thread. However the completion time of the ProcessFolder method is slower.

Any suggestion on how I can update the UI while still executing ProcessFolder on the main thread?

like image 254
RyeGuy Avatar asked Dec 24 '22 16:12

RyeGuy


2 Answers

Sometimes when your try update something on the main ui from within a method, depending on the way you've written (and/or structured) it can mean that the main dispatcher waits for the method to complete before updating the main ui.

If you were to try the following example it would successfully complete a UI update after each foreach iteration because when it completes it's initial task it wants to return out of the for loop and hits our main thread invoke, which HAS to complete before the loop can iterate again.

private void BeginProcess()
{
    Task.Run(()=>{
        for (int i = 0; i < 100; i++)
        {
            // Perform a task

            BeginInvokeOnMainThread(() => {
                iContent.Text = "UI Successfully updated: " + i " times out of 100";
            });
        }
    })
}

You can sometimes replicate this kind of effect by using:

NSRunLoop.Current.RunUntil(1000);

to allow the UI to catch up when you call the 'BeginInvokeOnMainThread' delegate.

like image 106
JoeTomks Avatar answered Jan 16 '23 00:01

JoeTomks


First of all, all the UI updates must be done in the main thread.

For your particular problem maybe you could use async/await (https://msdn.microsoft.com/library/hh191443(vs.110).aspx)

You could do something like in the main thread:

ProccessFolder testMethod = await ProccessFolder.Initialize(fileName);
iContent.Text = "UI Successfully updated.";

You have to make the Initialize method async and to return a task

like image 28
Constantin Iliescu Avatar answered Jan 15 '23 22:01

Constantin Iliescu