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です。

次に、そのエラー状況を修正しましょう。/ inを実行できます:trycatchOnNavigatedTo

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

しかし、私は実際には、ViewModel 中心の、データバインディングに適したエラー処理システムに傾倒しています。そうすれば、「切断」はアプリケーションにとって完全に自然な状態になります。エラー メッセージを表示するだけでも、アプリケーションは接続頻度の低いシステム (電話など) 向けに設計されていることになります。また、結果のコードはよりテストしやすくなります。

このアプローチについては、いくつかのブログ記事で説明しています。非同期初期化パターンについてはasyncコンストラクターに関する記事で取り上げ、特にデータ バインディングについてはプロパティに関する記事で取り上げていasyncます。データバインディングで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.IsFaulted悲しいInitialization.ErrorMessage場合などにバインドすることもできます。

于 2013-03-09T03:36:47.643 に答える