How to wait for an async call to complete on WP7? - windows-phone-7

I'm loading pivot items based on a call a webservice call. Given that all I have is asynchronous calls available, how do I go about catching when it's finished?
My main reason is that I'd like to keep a loading dialog up while it's waiting for the callback. However, I'm loading in a viewmodel class, and obviously the loading bar is in the page class.
Honestly, if I could just know when one pivot item was loaded, that would be fine, however setting an event handler on loadedpivotitem never seems to trigger.

I assume you are databinding your View to your ViewModel. In that case all you need to do is create a bool property and set it to true while loading/awaiting the async call. You could do something like this:
private bool isSyncing;
public bool IsSynchronizing
{
get { return this.isSyncing; }
set
{
this.isSyncing = value;
this.RaisePropertyChanged(() => this.IsSynchronizing); //Use appropriate RaisePropertyChanged method for your MVVM implementation
}
}
Before starting the async call you would set IsSynchronizing = true. At the end of the eventhandler set IsSynchronizing = false;
From your view you can bind to this bool. For the loadingbar it could be like this:
<ProgressBar Visibility="{Binding IsSynchronizing, Converter={StaticResource booleanToVisibilityConverter}}" IsIndeterminate="{Binding IsSynchronizing}" Style="{StaticResource PerformanceProgressBar}" />
In your scenario you can use an inverted BooleanToVisibilityConverter to hide the pivot while it is still loading.
Hope this helps, let me know if you need more info on using the BooleanToVisibilityConverters

You would need to hook up an event handler similar to as shown in this block of code:
public void LoadData()
{
SampleDataServiceClient client = new SampleDataServiceClient();
client.GetDataCompleted += new EventHandler<GetDataCompletedEventArgs>(client_GetDataCompleted);
client.GetDataAsync();
}
void client_GetDataCompleted(object sender, GetDataCompletedEventArgs e)
{
this.DataContext = e.Result;
}

Related

State of RefreshView is invoking RefreshCommand in .NET MAUI

I have a CollectionView in my .NET MAUI app and I placed it inside a RefreshView. When I call my API to populate this CollectionView, I cache the data so that I don't have to keep making API calls everytime the user hits this page.
In order to streamline my code, I created a private method in my view model that calls my API. The view model looks like this:
public partial MyViewModel : BaseViewModel
{
ObservableCollection<MyModel> MyData { get; } = new();
[RelayCommand]
async Task RefreshData()
{
IsBusy = true;
await GetData(true);
IsBusy = false;
}
private async Task GetData(bool shouldGetFreshData)
{
// Makes API call to get data, then assigns it to MyData collection
}
public async void Init()
{
IsBusy = true;
await GetData(false);
IsBusy = false;
}
}
The XAML for the page looks like this:
<RefreshView
IsRefreshing={Binding IsBusy}
Command={Binding RefreshDataCommand}>
<CollectionView>
...
</CollectionView>
</RefreshView>
I also wired the page to use the MyViewModel as its view model AND OnAppearing(), I call the Init() method of the view model.
Here's what I was expecting which is NOT what's happening:
I thought, the Init() would get called first which then calls the GetData() method with false input parameter. This way, I could use the cached data. And whenever, the user refreshes the CollectionView by pulling it down, the RefreshData() method would be called with true as the input parameter which would force the code to make an API call.
Instead of what I was expecting, here's what's happening:
The Init() method gets called first and as a result, the line with IsBusy = true executes.
This then ends up invoking the RefreshData() method
Then the await GetData(false) in Init() method executes
Then the await GetData(true) in RefreshData() method executes
As a result of all this, the GetData() method gets called twice.
I think, what's triggering this is the IsBusy. I thought IsBusy would only serve as an indicator but not necessarily invoke the RefreshData() method which is bound to the Command of my RefreshView.
Is this normal behavior or am I missing something here?
Apparently, this is "normal" behavior because I'm manually setting IsBusy to true. I decided to leave this question here because this may be a pitfall that affects others.
Here's the actual section in documentation that states this:
And here's the documentation: https://learn.microsoft.com/en-us/dotnet/maui/user-interface/controls/refreshview
So, all I had to do is remove the IsBusy = true in Init() method.

Catch API result - Xamarin Forms

i have this function in my shared project which i referenced to my new project. basically this function job is to fetch contact information from my api.
GetContact() function
i call it when i load my profile page and set the result object (contact) as my profile page bindingcontext.
calling the function
which i binded on my ui.
ui
my problem is, since fetching data on api is always async. my code execute and exit immediately even the result from api is not yet fetch. resulting into a null bindingcontext. is there any way that i can catch the api result and update my bindingcontext so that my be updated ui also?
For the async BindingContext I use the following:
XAML:
<Label Text="{Binding Type}"
TextColor="#142E5F"
Margin="10,0,0,0"
Font="Bold, 15"/>
ViewModel:
public int _type;
public int Type
{
get => _type;
set => SetProperty(ref _type, value);
}
With the SetProperty, you can change the value in execution time. You just need to asign the result.
public override async void OnAppering()
{
base.OnAppering();
Type = await APICall();
}

Activity Spinner in Xamarin doesn't instantly update

So I have this problem...
I have an activity Indicator in Xamarin set up like this
<ActivityIndicator IsRunning="{Binding IsSearching}" />
In my page class I bind it up like this...
private BindableProperty IsSearchingProperty =
BindableProperty.Create("IsSearching", typeof(bool), typeof(MainPage), false);
public bool IsSearching
{
get { return (bool)GetValue(IsSearchingProperty); }
set
{
SetValue(IsSearchingProperty, value) ;
}
}
Everythig works but only one problem. The spinner won't initialize before the method from which it is called is not finished... For example...
private async void New_Survey_Button_OnClicked(object sender, EventArgs e)
{
IsSearching = true;
some method that takes a long time...
await Navigation.PushAsync(new SurveyNavigationPage());
}
In this case the IsSearching true value will get initialized after the compiler hits await. Truly stuck with this issue. Please help. Thank you.
UPDATE 1___________________________
So after some debugging here is what exactly is going on.
1) IsSearching is hit which takes the program to the setter.
2) After it hits the setter it continues to the method that takes a long time.
3) Only after it is done with the time consuming method and goes out of the New_Survey_Button_OnClicked it actually hits the getter and updates the UI.

How to Cleanup a ViewModel in Mvvm Light?

I have a list of items that goes to another page, That page is hooked up to a view model. In the constructor of this view model I have code that grabs data from the server for that particular item.
What I found is that when I hit the back button and choose another item fromt hat list and it goes to the other page the constructor does not get hit.
I think it is because the VM is now created and thinks it does not need a new one. I am wondering how do I force a cleanup so that a fresh one is always grabbed when I select from my list?
I faced the same issue, that's how i solved it.
Have a BaseView class, override OnNavigatedTo
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
if (NavigatedToCommand != null && NavigatedToCommand.CanExecute(null))
NavigatedToCommand.Execute(null);
}
add DependencyProperty.
public static readonly DependencyProperty NavigatedToCommandProperty =
DependencyProperty.Register("NavigatedToCommand", typeof(ICommand), typeof(BaseView), null);
public ICommand NavigatedToCommand
{
get { return (ICommand)GetValue(NavigatedToCommandProperty); }
set { SetValue(NavigatedToCommandProperty, value); }
}
On the necessary pages, add to xaml (and, of course, inherit BaseView )
NavigatedToCommand="{Binding OnNavigatedToCommand}"
In the ViewModel, make command itself
public RelayCommand OnNavigatedToCommand
{ get { return new RelayCommand(OnNavigatedTo); } }
and implement method you want to call to update list
public async void OnNavigatedTo()
{
var result = await myDataService.UpdateMyList();
if (result.Status == OK)
MyList = result.List;
}
So, now, every time you navigate to page with list, inside of overriden OnNavigatedTo(), a NavigatedToCommand would be executed, which would execute OnNavigatedToCommand (which you set in xaml), which would call OnNavigatedTo, which would update your list.
A bit messy, but MVVM :)
EDIT: What about cleanings, they can be done in OnNavigatedFrom(), which works the same. Or OnNavigatingFrom(), which also can be useful in some cases.

ItemsControl.ItemsSource MVVM performance

I have an (non-virtualized) ItemsControl that binds its ItemsSource to a ObeservableCollection of ViewModel instances. Now once the large amount Model instances is loaded all the ViewModel complemnents needs to be added to that ObservableCollection. How can I add a large amount of ViewModels without making the UI Thread hang?
I suppose the UI Thread hangs because each time a new item is added the ItemsControl needs to update itself and does layout etc. over and over again.
Should I suspend the binding add all
items and then resume? If so, how?
Should I override the
ObservableCollection to implement an
AddRange so only 1 CollectionChanged
Event is fired for adding multiple
items? Or alternatively just replace
the whole collection?
Or is it better
to add each items separately and call
Dispatcher.Invoke for each item
separately? So I would unblock
frequently.
How do you handle large dynamic lists that can not be virtualized?
You can create a a class derived from ObservableCollection which allows you to temporarily suspend CollectionChanged events like this:
public class SuspendableObservableCollection : ObservableCollection
{
private bool suspended;
public bool Suspended
{
get
{
return this.suspended;
}
set
{
this.suspended = value;
OnCollectionChanged(new NotifyCollectionChangedEventArgs(
NotifyCollectionChangedAction.Reset));
}
}
protected override void OnCollectionChanged(
NotifyCollectionChangedEventArgs args)
{
if (!Suspended)
{
base.OnCollectionChanged(args);
}
}
}
<ItemsControl IsAsync="True" ... />

Resources