如何确保Xamarin/MAUI ViewModel数据在UI尝试使用它之前已经加载?

u0sqgete  于 2023-03-10  发布在  其他
关注(0)|答案(2)|浏览(209)

在我的应用程序中,我从REST API请求一个车辆集合,并将它们存储到一个属性中。这是使用异步任务完成的。我的页面使用绑定到这个可观察集合的选取器来使用它。目前,当页面加载时,根据使用速度的不同,选取器还没有填充。这通过等待一秒钟并重新打开选取器来解决它的问题。
我的假设是任务还没有完成,它运行异步意味着UI没有它继续运行。但是我不能肯定,因为我不知道如何在UI加载时测试它的值。
我想知道我可以采取什么措施来确保用户不会遇到这种类型的数据不加载。
简化视图模型

internal class BaseVehicleViewModel : BaseViewModel
    {
        public BaseVehicleViewModel()
        {
            Task.Run(async () => await GetAssets()).ConfigureAwait(false);

        }

        protected ObservableCollection<Asset> vehicles;

        public ObservableCollection<Asset> Vehicles
        {
            get => vehicles;
            set => SetProperty(ref vehicles, value);
        }

        protected async Task GetAssets()
        {
            var vehicles = await App.AppServiceClient.AssetsAsync();
            if (vehicles != null)
            {
                Vehicles =
                    new ObservableCollection<Asset>(vehicles.Where(v => v.Active == 1));
            }
        }

简化视图

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="App.Views.Page"
             xmlns:vm="clr-namespace:App.ViewModels">

    <ContentPage.BindingContext>
        <vm:ViewModel />
    </ContentPage.BindingContext>

    <ContentPage.Content>
        <Picker Title="Select a vehicle"
                ItemsSource="{Binding Vehicles}"
                ItemDisplayBinding="{Binding Reg}"
                SelectedItem="{Binding SelectedVehicle, Mode=TwoWay}"/>
    </ContentPage.Content>
</ContentPage>
moiiocjp

moiiocjp1#

我的假设是任务还没有完成,它运行异步意味着UI没有它就继续运行。
这是正确的代码当前执行加载作为一个即发即弃方法。即发即弃有几个问题:一个是不容易/不可能检测到它何时完成;另一个是(在这种情况下)忽略异常。
我想知道我可以采取什么措施来确保用户不会遇到这种类型的数据不加载。
用户总是会以某种方式体验数据加载,我建议你为你的页面设计一个“加载”状态。
建议阻塞异步工作。这将阻塞UI线程,提供非常糟糕的用户体验。用户只会看到一个非交互式的空白屏幕,操作系统会认为应用程序没有响应。如果这是一个移动的应用程序,那就更糟了,因为你只使用了一个应用程序就让整个屏幕都没有响应了;一些应用程序商店会自动测试这种行为,如果你的应用程序做了这种事情,就会拒绝它。
你需要设计用户体验来处理代码的异步特性,而不是阻塞或“发射后忘记”,这通常意味着同步地立即初始化到“加载”状态,然后用结果(或错误消息)更新用户界面。
我有一个old article,它提供了更多的细节,并建议一个类似于可绑定数据的Task<T>的类型,这个想法已经被复制到了几个库中:

它们的一般用法都是相同的,只是API稍有不同。例如,使用NotifyTask<T>

public BaseVehicleViewModel()
{
  Vehicles = NotifyTask.Create(async () => await GetAssetsAsync());
}

public NotifyTask<ObservableCollection<Asset>> Vehicles { get; }

protected async Task<ObservableCollection<Asset>> GetAssetsAsync()
{
  var vehicles = await App.AppServiceClient.AssetsAsync();
  if (vehicles == null)
    return new ObservableCollection<Asset>();
  return new ObservableCollection<Asset>(vehicles.Where(v => v.Active == 1));
}

现在,您的VehiclesNotifyTask<ObservableCollection<Asset>>而不是ObservableCollection<Asset>,您的数据绑定代码可以绑定到Vehicles.Result以绑定到ObservableCollection<Asset>(在加载资源之前是null),也可以绑定到Vehicles.IsNotCompleted以显示微调器/ backbone 而不是完整的UI,如果加载失败,则绑定到Vehicles.ErrorMessage,等等。

w1e3prcc

w1e3prcc2#

首先,可以尝试让async方法同步运行,比如:

public BaseVehicleViewModel()
{
   var task = Task.Run(async () => await GetAssets()).ConfigureAwait(false);
   task.Wait();
   // GetAssets().GetAwaiter().GetResult();
   // Task.Run(() => GetAssets().Wait());
}

此外,还可以使用EventToCommand将Page的OnAppearing事件转换为视图模型中的命令。
并且可以在ViewModel中声明一个flag来检查数据是否已经加载,比如:

internal class BaseVehicleViewModel : BaseViewModel
    {
        public bool finished = false;
        public BaseVehicleViewModel()
        {
           Task.Run(async () => await GetAssets()).ContinueWith(t=> { finished=true;});
           // when the task compete the finished flag will be set as true;
        }
        protected ObservableCollection<Asset> vehicles;
        public ObservableCollection<Asset> Vehicles
        {
            get => vehicles;
            set => SetProperty(ref vehicles, value);
        }

        protected async Task GetAssets()
        {
            var vehicles = await App.AppServiceClient.AssetsAsync();
            if (vehicles != null)
            {
                Vehicles =
                    new ObservableCollection<Asset>(vehicles.Where(v => v.Active == 1));
            }
        }

最后,您可以参考有关when to load the ViewModel for asynchronous data in a Xamarin Android app.的案例

相关问题