运行 视图模型中的延迟(异步)任务导致视图不更新

Running a delayed (async) task in view-model causes view to not update

我有一个配置文件视图模型,在它的构造函数中为刚刚登录的用户调用数据库。它工作正常,但如果我将它延迟 3 秒,那么我假设其中一个线程继续并创建内部没有任何内容的视图。我使用数据模板选择器来构造视图,如果我延迟任务,那么视图将不会呈现任何内容。同样,如果我不延迟它会正常工作,但我需要确保如果服务器需要更长的响应时间并且在某些情况下 3s 不是那么多,它会正常工作

这是我的视图模型

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;
        });
    }

风景

<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>

和数据库调用

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();
}

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

如果在Task中使用Await,从Await开始的代码会在另一个线程中,而不是在主线程中,所以UI不会更新。

您可以使用 Device.BeginInvokeOnMainThread(Action) Method 通过更改数据源来更新 UI。

我做了一个样例,你可以看看

 <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; }
}