15

我使用已经定义了视图模型的默认模板启动了一个电话应用程序。我修改了 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;
            }
        }

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

4

1 回答 1

36

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

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

首先,你应该避免async void. 我在我的异步编程最佳实践文章中详细描述了原因。在这种情况下,请考虑您的错误处理。很高兴您的快乐情况是“呼叫成功返回”,但悲伤的情况会破坏您的程序。

async Task所以,让我们尽可能地重写所有内容,并在我们这样做的时候遵循*Async约定:

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这是编写代码的更自然的方式。

接下来,让我们修复该错误情况。你可以做一个try/ catchin OnNavigatedTo

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

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

我在我的几篇博客文章中描述了这种方法:我在关于构造函数的文章中介绍了异步初始化模式,特别是在关于属性的文章中介绍了数据绑定。我编写了一个名为的帮助程序类,它使您能够与数据绑定一起使用。asyncasyncTaskCompletionNotifierTask

将这些设计落实到位,您的 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.IsFaulted悲伤Initialization.ErrorMessage的情况等。

于 2013-03-09T03:36:47.643 回答