0

I am coming from an Angular 2 and a C# back end background, so for the Angular side of things I am used to working with async functions and code, as well the C# background I understand the base libraries.

I am trying to create a simple page that has a a button, and a loading gif. You click the button the loading gif appears, 10 seconds later it disappears.

I can make the loading start no problem, but the nature of the async code jumps the execution and instantly makes the gif disappear.

How do I go about starting the spinner / making a gif visible, waiting 10 seconds in a non ui-blocking manner, and then finish with a thread-safe way of ending the animation / gif visibility?

View-Model code:

public class LoadingViewModel: INotifyPropertyChanged
    {
        private Visibility _loadingState;

        public event PropertyChangedEventHandler PropertyChanged = delegate { };

        public LoadingViewModel()
        {
            this._loadingState = Visibility.Collapsed;
        }

        public Visibility LoadingState
        {
            get {
                return this._loadingState;
            }
            set {
                this._loadingState = value;
                this.OnPropertyChanged();
            }
        }

        public void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            // Raise the PropertyChanged event, passing the name of the property whose value has changed.
            this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

MainView.xaml.cs:

public LoadingViewModel LoadingViewModel { get; set; }

        public MainPage()
        {
            this.InitializeComponent();
            this.LoadingViewModel = new LoadingViewModel();
        }


        private async Task BeginLoading()
        {
            LoadingViewModel.LoadingState = Visibility.Visible;
            await Task.Factory.StartNew(() =>
            {
                Task.Delay(TimeSpan.FromSeconds(10));

            }).ContinueWith(EndLoadingState);


        }

        //Updated and works but is there a better way?
        private async Task BeginLoading()
        {
            LoadingViewModel.LoadingState = Visibility.Visible;
            await Task.Factory.StartNew(async () =>
            {
                await Task.Delay(TimeSpan.FromSeconds(10));
                await EndLoadingState(); //<-- New EndLoadingState doesn't accept parms
            });


        }

        private async void EndLoadingState(object state)
        {
            await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => {
                LoadingViewModel.LoadingState = Visibility.Collapsed;
            });
        }

        private async void Button_Click(object sender, RoutedEventArgs e)
        {
            await BeginLoading();
        }

And lastly a basic stack panel with my button and image:

<StackPanel Margin="10,144,0,144">
            <Button Content="Begin Loading for 10 seconds" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0" Height="157" Width="366" FontSize="22" Background="{x:Null}" BorderThickness="5" BorderBrush="#FF58FF00" Click="Button_Click"/>
            <Image HorizontalAlignment="Center" Height="250" VerticalAlignment="Center" Width="250" Margin="0,25,0,0" Stretch="UniformToFill" Source="Assets/LoadingBubbles.gif" Visibility="{x:Bind Path=LoadingViewModel.LoadingState, Mode=TwoWay}"/>
        </StackPanel>
2
  • Did you check my answer? Commented Aug 4, 2017 at 7:01
  • Yes sorry I am reading right now I am at work and had to move on from this problem for a short time. Commented Aug 4, 2017 at 14:05

1 Answer 1

3

First, try using a bool property in your LoadingViewModel instead of Visibility as the latter is a UI attribute. You generally don't want that in your ViewModel. If your target version of Windows 10 is 14393 or higher, you can bind it directly without a BoolToVisibilityConverter. And the binding doesn't need to be TwoWay also.

 Visibility="{x:Bind Path=LoadingViewModel.IsLoading, Mode=OneWay}"

Second, XAML binding will actually take care of dispatching the updated value onto the UI thread. So you can also get rid of Dispatcher.RunAsync and have a normal void method

private void EndLoadingState(object state)
{
    LoadingViewModel.IsLoading = false;
}

Finally, your BeginLoading method(best to rename it to BeginLoadingAsync) can be simplified to

private async Task BeginLoadingAsync()
{
    LoadingViewModel.IsLoading = true;

    await Task.Delay(TimeSpan.FromSeconds(10));

    EndLoadingState();
}

Hope this helps!

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.