【问题标题】:Loading data in ViewModel asynchronously (with async and await) not working with databinding在 ViewModel 中异步加载数据(使用 async 和 await)不适用于数据绑定
【发布时间】:2013-02-24 15:47:52
【问题描述】:

我使用已定义视图模型的默认模板启动了一个电话应用程序。我修改了 MainViewModel 的 LoadData() 方法以异步调用 odata 服务。但它不适用于数据绑定。我已经验证调用成功返回但没有显示结果。

LongListSelector 的项目源绑定到视图模型中的 Items 属性。

<phone:LongListSelector ItemsSource="{Binding Items}" x:Name="MainLongListSelector" Margin="0,0,-12,0" SelectionChanged="MainLongListSelector_SelectionChanged">
                <phone:LongListSelector.ItemTemplate>
                    <DataTemplate>
                      <StackPanel Margin="0,0,0,17">
                            <TextBlock Text="{Binding UnReadCount}" TextWrapping="Wrap" Style="{StaticResource PhoneTextExtraLargeStyle}"/>
                            <TextBlock Text="{Binding description}" TextWrapping="Wrap" Margin="12,-6,12,0" Style="{StaticResource PhoneTextSubtleStyle}"/>
                      </StackPanel>
                    </DataTemplate>
                </phone:LongListSelector.ItemTemplate>
            </phone:LongListSelector>

这是我对视图模型的修改(注意 async 和 await 的用法):

public void LoadData()
    {
        FetchTileViewItems();        
    }

    private async void FetchTileViewItems()
    {
        var ret = await I2ADataServiceHelper.GetTileViewItemsAsync();
        this.Items = new ObservableCollection<TileViewItem>(ret);
        this.IsDataLoaded = true;
    }

我在页面上的 NavigatedTo 事件中调用 LoadData() 方法,就像以前一样:

protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            if (!App.ViewModel.IsDataLoaded)
            {
                App.ViewModel.LoadData();
                pr1.IsVisible = false;
            }
        }

点击运行,没有任何显示...我错过了什么吗?任何指针都非常感谢。

【问题讨论】:

  • pr1 是什么?不应该是pr1.IsVisible = true;吗?

标签: c# data-binding mvvm windows-phone-8 async-await


【解决方案1】:

好的,快速回答是您可能在Items 和/或IsDataLoaded 设置器上缺少INotifyPropertyChanged 通知。

更长的答案需要一点时间。 :)

首先,您应该避免使用async void。我在我的Best Practices in Asynchronous Programming 文章中详细描述了原因。在这种情况下,请考虑您的错误处理。很高兴您的快乐情况是“呼叫成功返回”,但悲伤的情况会破坏您的程序。

所以,让我们尽可能将所有内容改写为async Task,并在我们这样做的时候改写为follow the *Async convention

public async Task LoadDataAsync()
{
    await FetchTileViewItemsAsync();
}

private async Task FetchTileViewItemsAsync()
{
    var ret = await I2ADataServiceHelper.GetTileViewItemsAsync();
    this.Items = new ObservableCollection<TileViewItem>(ret);
    this.IsDataLoaded = true;
}

protected override async void OnNavigatedTo(NavigationEventArgs e)
{
    if (!App.ViewModel.IsDataLoaded)
    {
        await App.ViewModel.LoadDataAsync();
    }
}

这是编写async 代码的更自然的方式。

接下来,让我们修复该错误情况。您可以OnNavigatedTo 中执行try/catch

protected override async void OnNavigatedTo(NavigationEventArgs e)
{
    try
    {
        if (!App.ViewModel.IsDataLoaded)
        {
            await App.ViewModel.LoadDataAsync();
        }
    }
    catch (Exception ex)
    {
        ...
    }
}

但我实际上更倾向于使用以 ViewModel 为中心、数据绑定友好的系统来处理错误。这样,“断开连接”对于您的应用程序来说是一种非常自然的状态;即使它所做的只是显示一条错误消息,您的应用程序最终设计用于偶尔连接的系统(即电话)。此外,生成的代码更易于测试。

我在我的几篇博文中描述了这种方法:我在async 构造函数的帖子中介绍了asynchronous initialization pattern,在async 属性的帖子中介绍了data-binding in particular。我编写了一个名为 TaskCompletionNotifier 的帮助程序类,它使您能够将 Task 与数据绑定一起使用。

将这些设计落实到位,您的 ViewModel 代码最终看起来更像这样:

public sealed class MyViewModel : INotifyPropertyChanged
{
    public ObservableCollection<TileViewItem> Items
    {
      get { return _items; }
      private set { _items = value; RaisePropertyChanged(); }
    }

    public ITaskCompletionNotifier Initialization { get; private set; }

    public MyViewModel()
    {
        Initialization = TaskCompletionNotifierFactory.Create(InitializeAsync());
    }

    private async Task InitializeAsync()
    {
        var ret = await I2ADataServiceHelper.GetTileViewItemsAsync();
        this.Items = new ObservableCollection<TileViewItem>(ret);
    }
}

(这是假设您要开始在构造函数中加载数据。)

然后你可以直接绑定Items,也可以绑定Initialization.IsSuccessfullyCompleted为快乐的情况,Initialization.IsFaultedInitialization.ErrorMessage为悲伤的情况等等。

【讨论】:

  • 我相信 App.ViewModel.LoadData() 应该是 App.ViewModel.LoadDataAsync() (多次)
猜你喜欢
  • 2018-08-23
  • 2017-01-08
  • 1970-01-01
  • 2012-12-06
  • 2017-07-13
  • 2016-09-03
  • 1970-01-01
  • 2021-08-01
  • 2019-05-25
相关资源
最近更新 更多