0

I have a profile view-model that in it's constructor calls the DB for the user that just logged. It works fine but if I delay it by 3s then I assume one of the threads continues and creates the view with nothing inside. I use a data template selector to construct the view and if I delay the task then the view will not render anything. Again, if I don't delay it works fine but I need to make sure that it will work if the server takes longer to respond and 3s is not that much in some cases

here is my view model

public class ProfileViewModel: BaseViewModel
{
    private ObservableCollection<Stylist> users;
    public ObservableCollection<Stylist> Users
    {
        get => users;
        set
        {
            users = value;
            OnPropertyChanged();
        }
    }

    private Stylist user;
    public Stylist User { get => user; set { user = value; OnPropertyChanged(); } }

    private bool isStylist;


    public AsyncCommand TestCommand { get; set; }

    public ProfileViewModel()
    {
        IsBusy = true;
        TestCommand = new AsyncCommand(Test);
        Users = new ObservableCollection<Stylist>();
            
        Task.Run(async () =>
        {
            int id = ((App)App.Current).ID;

            if (!((App)App.Current).IsStylist)
            {
                var user = await DB.GetUser(id);
                User = CopyUserToStylist(user);
            }
            else User = await DB.GetStylist(id);
            isStylist = User.IsStylist;

            await Task.Delay(3000);      // without this line everything is ok    
            Users.Add(User);
            OnPropertyChanged(nameof(Users));
            IsBusy = false;
        });
    }

the view

<ContentPage.Resources>
    <DataTemplate x:Key="UserTemplate">
        <StackLayout>
            <Label Text="user logged"/>
            <Label Text="{Binding Name}"/>
        </StackLayout>
     </DataTemplate>

     <DataTemplate x:Key="StylistTemplate">
         <StackLayout>
             <Label Text="stylist logged"/>
             <Label Text="{Binding Name}"/>
             <Label Text="{Binding Location}"/>
                
             <Button Text="Test stylist"
                     Command="{Binding TestCommand}"/>
         </StackLayout>
     </DataTemplate>

     <local:UserTypeSelector
            x:Key="personDataTemplateSelector"
            StylistTemplate="{StaticResource StylistTemplate}"
            UserTemplate="{StaticResource UserTemplate}" />     

 </ContentPage.Resources>

 <ContentPage.Content>
     <StackLayout Margin="10"
                  BindableLayout.ItemTemplateSelector="{StaticResource personDataTemplateSelector}"
                  BindableLayout.ItemsSource="{Binding Users}">
         <Label Text="FML"/>
         <Label Text="Loading"
                IsVisible="{Binding IsBusy}"/>

         <Button Text="Test"
                 Command="{Binding TestCommand}"/>
     </StackLayout>
 </ContentPage.Content>

and the db calls

public static async Task<User> GetUser(int id)
{
    await Init();

    return  await db.Table<User>().Where(x => x.id == id).FirstAsync();
}

public static async Task<Stylist> GetStylist(int id)
{
    await Init();
    return await db.Table<Stylist>().Where(x => x.id == id).FirstAsync();
}
Bijington
  • 3,661
  • 5
  • 35
  • 52
Nicoara
  • 318
  • 2
  • 15
  • Please [note](https://learn.microsoft.com/en-us/dotnet/api/system.collections.objectmodel.observablecollection-1?view=net-5.0#remarks) that it is the object in the collection that must implement INotifyPropertyChanged (if only the property is changed without adding or removing the containing object). The ObservableCollection should be updated automatically when using methods such as Add, Remove, or Clear. – Benl Jul 30 '21 at 09:47
  • Have you thought about using this: https://stackoverflow.com/a/12520574/2598770 – Rand Random Aug 02 '21 at 06:59

1 Answers1

1

if I don't delay it works fine but I need to make sure that it will work if the server takes longer to respond and 3s is not that much in some cases

If you use Await in Task, the code starting from Await will be in another thread, not in main thread, so the UI will be not updated.

You can use Device.BeginInvokeOnMainThread(Action) Method to update UI by changing data source.

I do one sample that you can take a look.

 <StackLayout Margin="10" BindableLayout.ItemsSource="{Binding Users}">
            <BindableLayout.ItemTemplate>
                <DataTemplate>
                    <StackLayout>
                        <Label Text="{Binding name}" />
                        <Label Text="{Binding age}" />
                    </StackLayout>
                </DataTemplate>
            </BindableLayout.ItemTemplate>
        </StackLayout>

 public partial class Page52 : ContentPage
{
    
    public Page52()
    {
        InitializeComponent();
        this.BindingContext = new ProfileViewModel();
    }
}
public class ProfileViewModel:ViewModelBase
{
    public ObservableCollection<user1> Users { get; set; }
    public ProfileViewModel()
    {
        Users = new ObservableCollection<user1>();
        Task.Run(async()=> {
            user1 user = new user1() { name = "cherry", age = 18 };
            await Task.Delay(3000);
            Device.BeginInvokeOnMainThread(()=> {
                Users.Add(user);
            });
           
        
        });
    }
}
public class user1
{
    public string name { get; set; }
    public int age { get; set; }
}
Cherry Bu - MSFT
  • 10,160
  • 1
  • 10
  • 16