运行 视图模型中的延迟(异步)任务导致视图不更新
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; }
}
我有一个配置文件视图模型,在它的构造函数中为刚刚登录的用户调用数据库。它工作正常,但如果我将它延迟 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; }
}