2013-03-08 114 views
13

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

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> 

这里是我的修改视图模型(注意异步和等待使用):

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

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

+0

什么'pr1'?不应该是'pr1.IsVisible = true;'? – svick 2013-03-08 23:59:36

回答

33

好的,快速回答是,您可能会在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) 
    { 
     ... 
    } 
} 

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

我描述了几个我的博客文章的这种做法:我盖在我的岗位上async构造的asynchronous initialization pattern,并在我的岗位上async性能data-binding in particular。我写了一个名为TaskCompletionNotifier的助手类,它使您能够使用Task进行数据绑定。

把这些适当的设计,您的视图模型的代码最终看起来更像是这样的:

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为悲伤的情况下,等

+0

我相信App.ViewModel.LoadData()应该是App.ViewModel.LoadDataAsync()(多次) – Patric 2017-12-18 19:33:22

+0

@Patric:修正,谢谢! – 2017-12-18 19:41:00