I am trying to call some Azure Restful functions from .NET MAUI. No matter how I configure my await statements they never seem to alert my thread properly so that my retrieved data displays.
Specifically, I open a details page from a ScrollView using Navigation.PushAsync();
I have a ViewModel for my page with an Observable collection. When I create the collection I send it the key of the object I want back from the cloud. I format a URI to perform a GetAsync from an httpClient. I call the GetAsync, I check the status and I the read the result as a string:
try
{
httpHandler.response = await _restHandler.myClient.GetAsync(uri).ConfigureAwait(false);
httpHandler.response.EnsureSuccessStatusCode();
content = await httpHandler.response.Content.ReadAsStringAsync().ConfigureAwait(false);
Items = System.Text.Json.JsonSerializer.Deserialize<List<ProtoArcher>>(content);
if (Items.Count == 1)
{
_protoArcher = Items[0];
}
}
catch (Exception ex)
{
Debug.WriteLine(@"\tError {0}", ex.Message);
}
This code throws no exceptions. And it eventually returns my data in a few milliseconds. Elsewhere I have bound a ContentView to the ObservableCollection in some XAML. It looks like this...
ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="DRSMain.Views.RegisterArcher"
Title="RegisterArcher">
<ContentView x:Name="cvMain" Grid.Column="1" Grid.Row="2" Margin="10" >
<StackLayout BindableLayout.ItemsSource="{Binding archers}">
<BindableLayout.ItemTemplate>
<DataTemplate>
<StackLayout Orientation="Vertical">
<Label Text="Archer Details" VerticalOptions="End" />
<BoxView Color="Black" HeightRequest="3" VerticalOptions="Start" />
<Label Text="First Name" VerticalOptions="End" />
<Entry x:Name="firstName" VerticalOptions="End" Text="{Binding FirstName}"/>
<Label Text="Middle Name" VerticalOptions="End" />
<Entry x:Name="middleName" VerticalOptions="End" Text="{Binding MiddleName}" />
<Label Text="Last Name" VerticalOptions="End" />
<Entry x:Name="lastName" VerticalOptions="End" Text="{Binding LastName}"/>
<Label Text="Date of Birth" VerticalOptions="End"/>
<Entry x:Name="dateOfBirth" VerticalOptions="End" Text="{Binding DateOfBirth}"/>
<Label Text="Email" VerticalOptions="End" />
<Entry x:Name="email" VerticalOptions="End" Text="{Binding Email}"/>
<Label Text="Member Since" VerticalOptions="End" />
<Label x:Name="memberSince" VerticalOptions="End" Text="Today" />
<Label Text="Last Login" VerticalOptions="End" />
<Label x:Name="lastLogin" VerticalOptions="End" Text="Never" />
<Label Text="{Binding ArcherID}" />
</StackLayout>
</DataTemplate>
</BindableLayout.ItemTemplate>
<BindableLayout.EmptyView>
<StackLayout>
<Label Text="Archer Details" VerticalOptions="End" />
<BoxView Color="Black" HeightRequest="3" VerticalOptions="Start" />
<Label Text="First Name" VerticalOptions="End" />
<Entry VerticalOptions="End" Text="No Data"/>
</StackLayout>
</BindableLayout.EmptyView>
</StackLayout>
</ContentView>
Here is my View Model that has the observable archers object in it...
namespace DRSMain.ViewModels
{ public class RegisterArcherViewModel : INotifyPropertyChanged { Archer currentArcher; IList source;
public ObservableCollection<Archer> archers { get; private set; }
public RegisterArcherViewModel(string anArcherID)
{
currentArcher = new Archer();
source = new ObservableCollection<Archer>();
source.Add(currentArcher);
if (anArcherID != null)
{
currentArcher.ArcherID = anArcherID;
}
archers = new ObservableCollection<Archer>(source);
}
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
}
The behavior I see is that the thread getting the data is lost from the flow (the debugger never sees it) and page renders but without my fetched data being bound. When I rig it so the Observable collection has no data, I get the EmptyView, but when data comes back, I get the non-Empty layout and blank fields. I have tried everything I can think of. If I set up a timer and check the ObservableCollection, after the screen renders, the data is there, but it never gets displayed. Anyone have a suggestion for how to get this to work... seems like something everyone should be doing these days, call a service for data and display it on a page.... ugh....
I have tried forcing the OnPropertyChanged and I have tried using a timer to wake up and check for when the data has been received. I have then tried updating the UI and calling OnPropertyChanged to no avail....
While ObservableCollection automatically updates the UI upon add/remove, this may not always happen. Notifying the archers' property change explicitly after updating the collection may be helpful:
Update UI on Main Thread: When you update your UI or the underlying data that the UI is bound to, make sure these updates happen on the main thread using Microsoft.Maui.Dispatching.Dispatcher.Dispatch.