Hi,
I am very new to Xamarin.Forms and C# in general and have a very basic app that I cannot get working as I want it to. All the app has is 1 page, with an ActivityIndicator and a Button. Clicking the button starts a "long process" but is really just starting a for-loop with a delay that slowly increases the % complete (in the actual app I want to make, it will start a async process that stores a lot of data in a DB). Since this process will take awhile, I want the ActivityIndicator Text to gradually let the user know how far along in the process they are. The current app uses a Binding on the ActivityIndicator Text, this Text is updated via the Binding from the ViewModel. This is working when I just set the inital value to 0. However, when I start a for-loop with a Task.Delay() call inside it, the ActivityIndicator Text does not update when I set the value of the Binding variable from within the loop. If I do not add the Task.Delay() call, the Binding works and the ActivityIndicator gets set to 100%.
So I have a MainPage.xaml class that looks as follows:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
x:Class="ProgressUpdateTest.MainPage">
<AbsoluteLayout HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
<StackLayout IsVisible="{Binding IsBusy}"
Padding="12"
AbsoluteLayout.LayoutFlags="PositionProportional"
AbsoluteLayout.LayoutBounds="0.5,0.5,-1,-1">
<ActivityIndicator IsRunning="True"/>
<Label Text="{Binding PercentComplete, StringFormat='{0}% complete'}"
HorizontalOptions="Center"
TextColor="Black"/>
<Button Clicked="Button_Clicked"
Text="Start">
</Button>
</StackLayout>
</AbsoluteLayout>
</ContentPage>
And MainPage.xaml.cs looks like this:
namespace ProgressUpdateTest
{
[DesignTimeVisible(false)]
public partial class MainPage : ContentPage
{
public MainPageViewModel mainPageViewModel;
public MainPage()
{
InitializeComponent();
mainPageViewModel = new MainPageViewModel();
BindingContext = mainPageViewModel;
}
private void Button_Clicked(object sender, EventArgs e)
{
mainPageViewModel.Start();
}
}
}
With the ViewModel as follows:
namespace ProgressUpdateTest.ViewModel
{
public class MainPageViewModel
{
private bool _isBusy;
public bool IsBusy
{
get { return _isBusy; }
set
{
_isBusy = value;
}
}
private string _percentComplete;
public string PercentComplete
{
get { return _percentComplete; }
set
{
_percentComplete = value;
}
}
private MainPageUseCase mainPageUseCase;
public MainPageViewModel()
{
mainPageUseCase = new MainPageUseCase();
IsBusy = true;
PercentComplete = "0";
}
public async void Start()
{
for (var i = 1; i < 101; i++)
{
await Task.Delay(500);
Console.WriteLine(i);
Device.BeginInvokeOnMainThread(() =>
{
PercentComplete = i.ToString();
});
}
IsBusy = false;
}
}
}
The initial PercentComplete = 0
works, but the ones in the Loop do not. I have tried everything I could think of to debug this issue, but have had no luck. I believe this is happening because of my lack of knowledge about async and things of that sort with C#. I have tried this without the Device.BeginInvokeOnMainThread()
call, changing the await Task.Delay(500)
line to Task.Delay(500).Wait()
, and a few other things and have not seen the ActivityIndicator move from 0%. The issue definitely has to do with the await Task.Delay(500)
line, if I remove that then I can see the PercentComplete
variable change when I change it to something after the initial PercentComplete = 0
.
If anyone has any idea about what is going on and how I can fix it I would appreciate some help!
Thank you,
Ryan