How to make activity indicator wait for a function - c#

I would like to use activity indicator to show that my function is loading. It goes so fast that I can see my activity inndicator but the function have not finished loading
Question : How to use make my activity indicator to false when the functions have really finished to run
Here my code :
public MainPage()
{
InitializeComponent();
On<iOS>().SetUseSafeArea(true);
activityIndicator.IsRunning = true;
MyPageName = "home";
var tasks = new List<Task>();
tasks.Add(Task.Run(async () => {
GetMyLeafLanguage(); //p
}));
tasks.Add(Task.Run(async () => {
if (App.CheckConnection() == true)
{
MyAds(); // doit être apres l'initialisation //
LoadtestPage();
}
}));
Task.WhenAll(tasks);
#endregion My Initial Tasks
OnGoTutorial();
MyNotificationActivate();
activityIndicator.IsRunning = false;
}

The way you wrote it, the tasks had to finish their work, before the page could display. So the page (finally) displayed, but since the work was already done, the activity indicator immediately disappeared.
These functions should run after the page is displayed.
Otherwise, while the tasks run, user will be looking at a blank screen (or at whatever page was showing before you starting creating this page).
Unfortunately, there is no cross-platform event that runs after a page displays for the first time.
Here is one way to let the page display while the background work is being done:
public partial class MyPage : ...
{
protected override void OnAppearing()
{
base.OnAppearing();
BeginWorkWithIndicator();
}
private void BeginWorkWithIndicator()
{
activityIndicator.IsRunning = true;
// The delay gives Xamarin UI a little time, before your background work begins.
// Without this delay, under some circumstances, the page might not show up as quickly.
Device.StartTimer(TimeSpan.FromMilliseconds(100), () => {
OneTimeBackgroundWork();
// Run timer only once.
return false;
});
}
private void OneTimeBackgroundWork()
{
... Long-running work - MUST NOT TOUCH UI ...
// When done.
Device.BeginInvokeOnMainThread(() => {
// NOW MAKE UI CHANGES, based on variables set by work above.
...
activityIndicator.IsRunning = false;
});
}
}

Related

Task.Run then Invoke on main thread alternative using async await ContinueWith?

The following code works perfectly. It shows the spinner on the UI, starts a task using a thread from the threadpool and runs the heavy operation, once complete, logic to hide the spinner executes on the main thread as intended.
public void LoadCustomers()
{
// Update UI to show spinner
this.LoadingCustomers = true;
Task.Run(async () =>
{
var customers = await this.custService.GetCustomers();
// code truncated for clarity
Device.BeginInvokeOnMainThread(() =>
{
// Update UI to hide spinner
this.LoadingCustomers = false;
});
});
}
My question; Is there a better way to write this logic using ContinueWith/ConfigureAwait options? Using these options seems to block the UI thread. In the example below, shouldn't the UI thread continue running the UI logic (animating the spinner/user input) and then come back to complete the logic inside the ContinueWith?
public void LoadCustomers()
{
// Update UI to show spinner
this.LoadingCustomers = true;
this.custService.GetCustomers().ContinueWith((t) =>
{
var customers = t.Result;
// code truncated for clarity
// Update UI to hide spinner
this.LoadingCustomers = false;
});
}
As requested in the comments, here is the code for GetCustomers. the dbContext is EntityFrameworkCore.
public async Task<List<CustomerModel>> GetCustomers()
{
return await this.dbContext.Customers.ToListAsync();
}
UPDATE
The answer by FCin is correct, however; the cause root of this seems to be with EFCore and ToListAsync, it isn't running asynchronously.
Proper way of writing such method is to use async/await from start to finish. Right now you are doing fire and forget meaning if there is exception inside Task.Run you will never know about it. You should start from an event handler. This can be whatever, mouse click, page loaded, etc.
private async void MouseEvent_Click(object sender, EventArgs args)
{
await LoadCustomers();
}
public async Task LoadCustomers()
{
// Update UI to show spinner
this.LoadingCustomers = true;
// We don't need Device.BeginInvokeOnMainThread, because await automatically
// goes back to calling thread when it is finished
var customers = await this.custService.GetCustomers();
this.LoadingCustomers = false;
}
There is an easy way to remember when to use Task.Run. Use Task.Run only when you do something CPU bound, such as calculating digits of PI.
EDIT: #bradley-uffner suggested to just write the following:
public async Task LoadCustomers()
{
// Update UI to show spinner
this.LoadingCustomers = true;
var customers = await this.custService.GetCustomers();
// code truncated for clarity
// you are still on UI thread here
this.LoadingCustomers = false;
}
How about this:
public async Task LoadCustomers()
{
// Update UI to show spinner
this.LoadingCustomers = true;
await Task.Run(async () =>
{
var customers = await this.custService.GetCustomers();
// code truncated for clarity
});
this.LoadingCustomers = false;
}
The code after await is executed on the current thread so it should work out of the box.

How do I update the UI from a method in my ViewModel before the method finishes? [duplicate]

This question already has answers here:
Execute task in background in WPF application
(3 answers)
Closed 5 years ago.
I have a MainWindow.xaml that gets populated with UserControls. I have a control in the MainWindow.xaml that has a button. The button is bound to the following command in MyViewModel.cs:
public ICommand MyCommand
{
get
{
return Get(() => MyCommand, () => new RelayCommand(ExecuteMyCommand, CanExecuteMyCommand));
}
}
public void ExecuteMyCommand()
{
Messenger.Default.Send(new NotificationMessage(this, MainWindowMessageBus.ShowPopup1), MainWindowMessageBus.Token);
Method1();
Messenger.Default.Send(new NotificationMessage(this, MainWindowMessageBus.ShowPopup2), MainWindowMessageBus.Token);
Method2();
}
public bool CanExecuteInsertBedCommand()
{
return true;
}
Method1() and Method2() execute code that take about 5 seconds each. The messengers are sending messages to the codebehind MainWindow.xaml.cs:
Popup1 popup1;
Popup2 popup2;
private void NotificationMessageReceived(NotificationMessage msg)
{
switch(msg.Notification)
{
case MainWindowMessageBus.ShowPopup1:
popup1 = new Popup1();
ControlContainer.Children.Add(popup1);
break;
case MainWindowMessageBus.ShowPopup2:
// Remove popup1
ControlContainer.Children.Remove(popup1);
popup1 = null;
// Add popup2
popup2 = new Popup2();
ControlContainer.Children.Add(popup2);
break;
default:
break;
}
}
Expected results: upon pressing my button, Popup1 shows immediately and stays in the window while Method1() executes. Then, Popup2 replaces Popup1 in the window and Method2() executes.
Actual results: upon pressing my button, nothing happens in the UI. Method1() and Method2() execute as expected, and after they both run (~10 seconds), Popup2 is displayed. I never see Popup1.
I tried a couple threading/async methods, but they didn't work. How can I get ExecuteMyCommand() to update the UI as it goes?
EDIT
Ok so I can't answer my own question for some reason. I don't think this question is a duplicate of the specified thread, since that would imply that I already knew I needed to run Method1() in the background, which I did not. Anyways, here's what I changed:
public async void ExecuteMyCommand()
{
Messenger.Default.Send(new NotificationMessage(this, MainWindowMessageBus.ShowPopup1), MainWindowMessageBus.Token);
await Task.Run(() =>
{
Method1();
});
Messenger.Default.Send(new NotificationMessage(this, MainWindowMessageBus.ShowPopup2), MainWindowMessageBus.Token);
Method2();
}
Don't call a method who take several time in the UI thread.
You can try by created a custom window.
In this you can show all custom information for your user.
As your method take several time, you can add a cancel button for exemple.
You can also add a progress bar. When a method take time, it's great to know the state of the system.
Try this, when you press your button -> call a command to lunch a thread
In your command :
void Command()`
{
// run a thread, create a window and show it
// you can declare the thread outside if you want use something like join
Thread threadExecute = new Thread(Execute);
w = new WinTest();
w.Topmost = true;
w.Show();
threadExecute.Start();
}
// Call when your thread terminated
void finish()
{
w.Close();
}
void Execute()
{
// Your big method 1
// Change the window information
// big method 2
this.Dispatcher.Invoke( finish );
}

Update UI before actual execution of method

I want a loading indicator to start immediately before the execution of a method. The execution of the method involves the work of entity framework so I don't (can't) put that type of code in a new thread bc entity framework isn't thread safe. So basically in the method below, I want the first line to execute and have the UI update and then come back and execute the rest of the code. Any ideas?
public async void LoadWizard()
{
IsLoading = true; //Need the UI to update immediately
//Now lets run the rest (This may take a couple seconds)
StartWizard();
Refresh();
}
I can't do this:
public async void LoadWizard()
{
IsLoading = true; //Need the UI to update immediately
await Task.Factory.StartNew(() =>
{
//Now lets run the rest (This may take a couple seconds)
StartWizard();
Refresh(); //Load from entityframework
});
//This isn't good to do entityframework in another thread. It breaks.
}
You can invoke empty delegate on UI dispatcher with priority set to Render, so that UI process all the queued operations with equal or higher priority than Render. (UI redraws on Render dispatcher priority)
public async void LoadWizard()
{
IsLoading = true; //Need the UI to update immediately
App.Current.Dispatcher.Invoke((Action)(() => { }), DispatcherPriority.Render);
//Now lets run the rest (This may take a couple seconds)
StartWizard();
Refresh();
}
Assuming your busy indicator visibility is bound to IsLoading property, you are doing "something" wrong in StartWizard or Refresh method. Your StartWizard and Refresh methods should only load data from your data source. You must not have any code that changes the state of UI in your loading methods. Here is some pseudocode..
public async void LoadWizard()
{
IsLoading = true;
StartWizard();
var efData = Refresh();
IsLoading = false;
//update values of properties bound to the view
PropertyBoundToView1 = efData.Prop1;
PropertyBoundToView2 = efData.Prop2;
}
public void StartWizard()
{
//do something with data that are not bound to the view
}
public MyData Refresh()
{
return context.Set<MyData>().FirstOrDefault();
}

Block UI thread but keep handling incoming calls

I'm developing a plugin for a 3D modelling application. For this application, there is also a third party plugin (a render engine) that I would like to automate.
What I do is create a list of Camera List<Camera> cameraViews , iterate trough all of them and tell the render engine to start rendering
foreach ( Camera camera in cameraViews )
{
// tell the modellingApplication to apply camera
modellingApplication.ApplyCameraToView(camera);
// tell the render engine to render the image
string path = "somePathWhereIWantToSaveTheImage"
renderEngine.renderCurrentScene(path)
// .renderCurrentScene() seems to be async, because my code, which is on the UI thread
// continues... so:
// make sure that the image is saved before continuing to the next image
while ( !File.Exists(path) )
{
Thread.Sleep(500);
}
}
However, this wont work. The renderingplugin seems to do some async work but, when doing this async work, it is calling the main thread for retrieving information.
I found a workaround for this: Right after calling the render engine to render, call a MessageBox. This will block the code from continuing but async calls are still beïng handled. I know, this is weird behaviour. Whats even weirder is the fact that my MessageBox gets automatically closed when the renderengine has done calling the UI thread for information and continues in his own process. Making my code continue to the while loop to check if the image is saved on the disk.
foreach ( Camera camera in cameraViews )
{
// tell the modellingApplication to apply camera
modellingApplication.ApplyCameraToView(camera);
// tell the render engine to render the image
string path = "somePathWhereIWantToSaveTheImage"
renderEngine.renderCurrentScene(path)
// .renderCurrentScene() seems to be async, because my code, which is on the UI thread
// continues... so:
// show the messagebox, as this will block the code but not the renderengine.. (?)
MessageBox.Show("Currently processed: " + path);
// hmm, messagebox gets automatically closed, that's great, but weird...
// make sure that the image is saved before continuing to the next image
while ( !File.Exists(path) )
{
Thread.Sleep(500);
}
}
This is wonderful, except for the messagebox part. I don't want to show a messagebox, I just want to pause my code without blocking the entire thread (as calls from the renderengine to the ui thread are still accepted)..
It would've been much easier if the renderengine didn't do his work async..
I don't feel this is the best answer, but it hopefully it's what you are looking for. This is how you block a thread from continuing.
// Your UI thread should already have a Dispatcher object. If you do this elsewhere, then you will need your class to inherit DispatcherObject.
private DispatcherFrame ThisFrame;
public void Main()
{
// Pausing the Thread
Pause();
}
public void Pause()
{
ThisFrame = new DispatcherFrame(true);
Dispatcher.PushFrame(ThisFrame);
}
public void UnPause()
{
if (ThisFrame != null && ThisFrame.Continue)
{
ThisFrame.Continue = false;
ThisFrame = null;
}
}
If you want to still receive and do actions on that thread while blocking intermediately, you can do something like this. This feels, um... kinda hacky, so don't just copy and paste without making sure I didn't make some major mistake typing this out. I haven't had my coffee yet.
// Used while a work item is processing. If you have something that you want to wait on this process. Or you could use event handlers or something.
private DispatcherFrame CompleteFrame;
// Controls blocking of the thread.
private DispatcherFrame TaskFrame;
// Set to true to stop the task manager.
private bool Close;
// A collection of tasks you want to queue up on this specific thread.
private List<jTask> TaskCollection;
public void QueueTask(jTask newTask)
{
//Task Queued.
lock (TaskCollection) { TaskCollection.Add(newTask); }
if (TaskFrame != null) { TaskFrame.Continue = false; }
}
// Call this method when you want to start the task manager and let it wait for a task.
private void FireTaskManager()
{
do
{
if (TaskCollection != null)
{
if (TaskCollection.Count > 0 && TaskCollection[0] != null)
{
ProcessWorkItem(TaskCollection[0]);
lock (TaskCollection) { TaskCollection.RemoveAt(0); }
}
else { WaitForTask(); }
}
}
while (!Close);
}
// Call if you are waiting for something to complete.
private void WaitForTask()
{
if (CompleteFrame != null) { CompleteFrame.Continue = false; }
// Waiting For Task.
TaskFrame = new DispatcherFrame(true);
Dispatcher.PushFrame(TaskFrame);
TaskFrame = null;
}
/// <summary>
/// Pumping block will release when all queued tasks are complete.
/// </summary>
private void WaitForComplete()
{
if (TaskCollection.Count > 0)
{
CompleteFrame = new DispatcherFrame(true);
Dispatcher.PushFrame(CompleteFrame);
CompleteFrame = null;
}
}
private void ProcessWorkItem(jTask taskItem)
{
if (taskItem != null) { object obj = taskItem.Go(); }
}

How to make a function to wait for a specific time in Windows phone?

I am building a Windows phone application with lots of animations. What I need is that when the application starts, it should be done finishing the first animation, say myS() is the name of the storyboard. Only after it is done with the animation, a textblock should appear after 3 seconds. What methods should I use to make the display of textbox wait for the Storyboard to finish?
What I tried is using Thread.Sleeps(10000) but that doesn't work. This is the code -
public partial class MainPage : PhoneApplicationPage
{
// Constructor
public MainPage()
{
InitializeComponent();
c1.Visibility = Visibility.Collapsed; //c1 is the name of the textbox
myS.Begin();
canDisp();
}
private void e1_ManipulationStarted(object sender, ManipulationStartedEventArgs e)
{
myS1.Begin();
}
private void e1_ManipulationCompleted(object sender, ManipulationCompletedEventArgs e)
{
myS2.Begin();
}
void canDisp()
{
c1.Visibility = Visibility.Visible;
}}
After the myS.Begin() is done executing, I want the program to wait for 3 seconds & then execute the canDisp() method, how can I achieve that?
If your animation is a Storyboard then it has a Completed event (MSDN link) for just this purpose.
Your call to Thread.Sleep() was probably being run on the same thread as the animation and so was stopping the animation from running while it was sleeping.
If you really want to go down this route then you'll need to move your sleep call to a different thread.
If you really want to use threads, a simple way to do it is:
System.Threading.ThreadPool.QueueUserWorkItem(obj =>
{
System.Threading.Thread.Sleep(5000);
Dispatcher.BeginInvoke(() =>
{
MessageBox.Show("after delay");
});
});
I am using a windows store app where System.Threading.ThreadPool.QueueUserWorkItem
is not available.So I did like this and not working as expected
await Windows.System.Threading.ThreadPool.RunAsync(async (s) =>
{
await Task.Delay(1);
await MainViewModel.Instance.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.High, () =>
{
MessageBox.Show("after delay");
});
}, Windows.System.Threading.WorkItemPriority.High);

Categories