【问题标题】:What is the best way to fetch data while costructing a Xamarin Page?在构建 Xamarin 页面时获取数据的最佳方法是什么?
【发布时间】:2019-05-12 17:34:05
【问题描述】:

我正在构建一个与 BLE 设备对话的 Xamarin 应用程序。目前我正在构建类似“文件浏览器”的东西;我有一个页面,其中包含可以从设备下载的 3 种“文件类型”的列表视图;当一个项目被点击时,我想创建一个新页面(使用Navigation.PushAsync()),其中包含从设备获取的文件列表。我正在使用一个页面来实现它,该页面在其构造函数中创建并绑定到其视图模型,但我无法在构造函数中调用异步方法来下载数据。

这是糟糕的设计还是有办法在创建视图模型期间开始下载信息?

【问题讨论】:

    标签: c# xamarin mvvm xamarin.forms


    【解决方案1】:

    当您的数据 ID 被下载时,您可以打开新页面,其中包含一个绑定到 YourViewModel 中的 IsBusy 属性的活动指示器。

    然后您可以开始下载新任务,如下所示:

    public class YourViewModel
    {
        private bool isBusy;
        public bool IsBusy
        {
            get => isBusy;
            set => SetProperty(isBusy, value);
        }
    
        public YourViewModel()
        {
            StartDownload();
        }
    
        private void StartDownload()
        {
            IsBusy = true;
    
            Task.Run(ExceptionHandler(async() => 
                {
                  // Your download starts here
                  // await it
                  // and when it came finished:
                  Device.BeginInvokeOnMainThread(() => IsBusy = false);
                }));
        }
    
        private void ExceptionHandler(Action action)
        {
            try
            {
                action?.Invoke();
            }
            catch(Exception ex)
            {
                // Handle exceptions here
            }
        }
    }
    

    您可以通过视图模型中的事件或类似的方式添加页面组件。

    希望对你有帮助。

    【讨论】:

    • 我不知道BeginInvokeOnMainThread;仅供参考,我是否也可以调用isBusy = false; 而不是在主线程上执行的IsBusy = false;
    • @ArenaLor,这不是线程的问题,但是如果你使用isBusy = true,你的View不会被通知属性变化,只有在更新属性时才会触发,假设当然,您正在使用一些 MVVM 框架或从 IsBusy 属性的 set 访问器手动调用 PropertyChanged (我想,给定答案的 SetProperty 方法就是这样做的,即更改 isBusy 的值并通知客户端,实际上来了内置许多框架)。
    • @FrancescoBlue 回答得很好,谢谢。 SetProperty 是关于通知属性更改的。我还假设您使用的是 MVVM 方法。
    • 谢谢@FrancescoBlue 和@Diego Rafael Souza;我对此有一个小问题;如果在下载期间(在Task.Run 内)抛出异常,尽管我使用了try/catch 块,但未捕获异常
    • @ArenaLor 你不能用线程外的try\catch 块来处理异常。例如,您可以按照我的建议进行操作。
    【解决方案2】:

    作为替代方法,您可以在 ViewModel 中定义 LoadDataAsync 方法,并从视图的 OnAppearing 方法中调用它。您必须将其标记为异步:

    public class MyView : ContenPage {
    
        private bool isDataLoaded = false;
    
        public void MyView {
            BindingContext = new MyViewModel();
        }
    
        protected async override void OnAppearing(){
            if (!isDataLoaded && BindingContext is MyViewModel vm){
                await vm.LoadDataAsync();
                isDataLoaded = true;
            }
        }
    }
    

    注意isDataLoaded 字段:在获取数据后将其设置为true 避免在从另一个页面导航返回时再次获取数据(这可能有用也可能不会有用,具体取决于您的应用程序逻辑)。

    在 ViewModel 中的 LoadDataAsync 方法中,您可以将获取数据的代码插入到普通的 try / catch 块中。

    【讨论】:

    • 唯一的问题是调用await vm.LoadDataAsync();后列表没有更新;我应该做其他事情来“刷新”该属性吗?
    • 您是否在 ViewModel 中正确实现了 INotifyPropertyChanged?
    • 是的,我在所有 ViewModel 中都使用相同的 INotifyPropertyChanged,没有任何问题。我使用 viewModel 中的另一个属性 IsDownloading 来控制 ActivityIndi​​cator 应该是可见的,并且工作正常:微调器在数据下载期间可见并正在运行,但是列表似乎为空(与我调用时相比)使用 Task.Run 从构造函数下载方法被正确加载)
    • 似乎在Device.BeginInvokeOnMainThread 方法内的列表中添加是有效的。
    • 是的,理论上影响 UI 刷新的属性更新应该在主线程上完成。但实际上我多次使用这种模式而没有担心这一点,一切都很好。不过,它总是在 Android 上。也许你在 iOS 上工作?我听说 iOS 对请求所有与 UI 相关的东西总是在主线程上更加挑剔。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-09-15
    • 1970-01-01
    • 2022-08-23
    • 2016-01-09
    • 2013-05-01
    • 2021-05-12
    • 2018-03-21
    相关资源
    最近更新 更多