質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

91.78%

  • C#

    3900questions

    C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

  • Xamarin

    254questions

    Xamarin(ザマリン)は、iPhoneなどのiOSやAndroidで動作し、C# 言語を用いてアプリを開発できるクロスプラットフォーム開発環境です。Xamarin Studioと C# 言語を用いて、 iOS と Android の両方の開発を行うことができます。

  • MVVM

    46questions

    MVVM(Model View ViewModel)は構築上のデザインパターンで、表現ロジック(ViewModel)によってデータ(Model)からページ(View)を分離させます。

【C#】【MVVM】非同期でObservableCollectionに追加したデータをViewに反映させる方法が知りたい

解決済

回答 4

投稿 2017/07/09 20:01

  • 評価
  • クリップ 0
  • VIEW 311

OXamarin

score 5

前提・実現したいこと

取得したツイートをViewにバインドしたい

発生している問題

2点あります。

・1点目
ViewModelのコンストラクタ内で初期表示するツイートを取得しています。
Timelineプロパティの値をViewにバインディングさせています。
このプロパティはObservableCollectionなので、Addされたタイミングで変更通知がいっていると思いますが実際には何もバインドされません。

・2点目
コンストラクタの中で実行している Task.Run内の処理がawaitしているにも関わらずにすぐに処理が通り抜けた後に await内の処理が実行されています。なぜこのような動作になってしまうのでしょうか。

Code (ViewModel)

public class MainPageViewModel : BindableBase
{
    #region プロパティ
    public ObservableCollection<string> TweetList { get; set; }
    public ObservableCollection<long> UserListIds { get; set; }
    public ObservableCollection<Timeline> Timeline { get; set; }
    #endregion

    #region フィールド
    private Tweet _tweet = new Tweet();
    #endregion

    #region コンストラクタ
    public MainPageViewModel()
    {
        Task.Run(async () =>
        {
            await _tweet.GetListIdsAsync("ユーザ名");
            UserListIds = _tweet.UserListIds;
            await _tweet.GetListTimelineAsync(UserListIds.Skip(2).First());
            Timeline = _tweet.Timeline;
        });
    }
    #endregion
}

Code (View)

<ListView ItemsSource="{Binding Timeline}"
          RowHeight="80">
    <ListView.ItemTemplate>
        <DataTemplate>
            <ViewCell>
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="10" />
                        <RowDefinition Height="20" />
                        <RowDefinition Height="40" />
                    </Grid.RowDefinitions>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="50" />
                        <ColumnDefinition Width="*" />
                        <ColumnDefinition Width="50" />
                    </Grid.ColumnDefinitions>

                    <!--トーク相手の画像-->
                    <Image Source="{Binding ProfileImage}"
                           Grid.RowSpan="3"
                           />
                    <!--トーク相手の名前-->
                    <Label Grid.Row="1" Grid.Column="1"
                           Text="{Binding Name}" 
                           FontAttributes="Bold"
                           />
                    <!--最終トーク内容-->
                    <Label Grid.Row="2" Grid.Column="1"
                           Text="{Binding Text}"
                           FontSize="12"
                           />
                </Grid>
            </ViewCell>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

試したこと

・Twitter APIからデータを取得するメソッドを同期的にして試してみましたが、対応していないのか例外で落ちてしまいます。
・コンストラクタ内でTimelineプロパティに対してAddをしてみると画面に反映されました。

GitHub

全体のコードを以下のGitHubにアップロードしています
https://github.com/karimatan1106/XamarinTweetForList

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 4

checkベストアンサー

+3

・1点目

Timeline = _tweet.Timeline;


これではTimeline のインスタンスを書き換えてしまっているので変更が通知されません。

Timeline.Add(_tweet.Timeline.Last());


こんな感じでAdd すれば変更が通知されると思います。

・2点目

        Task.Run(async () =>
        {
            await _tweet.GetListIdsAsync("ユーザ名");
            UserListIds = _tweet.UserListIds;
            await _tweet.GetListTimelineAsync(UserListIds.Skip(2).First());
            Timeline = _tweet.Timeline;
        });


Task.Run をawait していないからでは?
Task.Run 内部のデリゲートだけでいいと思います。
ただ、非同期処理をコンストラクタで実行するよりは、何らかのトリガー(ボタンをタップするなど)を持って実行すべきかと思います。

なので、コンパイルはしていないですが以下のようになるのではないでしょうか。

await _tweet.GetListIdsAsync("ユーザ名");
UserListIds.Add(_tweet.UserListIds.Last());
await _tweet.GetListTimelineAsync(UserListIds.Skip(2).First());
Timeline.Add(_tweet.Timeline.Last());

投稿 2017/07/10 00:11

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    以下のような回答は評価を下げられます

    • 間違っている回答
    • 質問の回答になっていない投稿
    • 不快な投稿

    評価を下げる際はその理由をコメントに書き込んでください。

+1

下記記事のように、非同期処理内で配列にアクセスする処理を、Device.BeginInvokeOnMainThreadに渡して実施してみてはいかがでしょうか。

http://running-cs.hatenablog.com/entry/2016/07/26/220732

投稿 2017/07/09 22:28

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    以下のような回答は評価を下げられます

    • 間違っている回答
    • 質問の回答になっていない投稿
    • 不快な投稿

    評価を下げる際はその理由をコメントに書き込んでください。

+1

1点目

ViewにBindしているのはMainPageViewModel のTimelineなのに、取得した情報をAddしているのは_tweetインスタンスの Timelineだからでは? Timeline = _tweet.Timeline を有効にしたいのであれば、

//MainPageViewModel 
ObservableCollection<Timeline> _timeline;
public ObservableCollection<Timeline> Timeline 
{ 
    get{ return _timeline;} 
    set
    {
        if( _timeline == value )
            return;
        _timeline = value;
        OnPropertyChanged();
    }
}


のようにObservableCollection自体が差し替えられたことを通知してあげる必要があるかと。

2点目

GetListIdsAsync内で何をやっているか追えていないですが、

await Task.Run(async () =>
{
  await task1();
  await task2();
});


じゃないでしょうか? Task.Run内でのawait って私はやらないパターンですが。。

投稿 2017/07/10 09:44

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    以下のような回答は評価を下げられます

    • 間違っている回答
    • 質問の回答になっていない投稿
    • 不快な投稿

    評価を下げる際はその理由をコメントに書き込んでください。

+1

まずUserListIdsやTimelineはその書き方ですと実体が定義されてないでnullになっていると思いますがそれは意図通りでしょうか?
Task.Runの中のTimeline=_tweet.Timelineで_tweet.TimelineがさしているインスタンスがMainPageViewModelのTimelineにもセットされますがこれも意図通りでしょうか?
本来やりたいことはMainPageViewModel内でTimelineとして扱われるObservableCollectionを生成し、そこに取得されたtweetの中身をコピーすることなのではと思いますが、そうであるならばこのコードでは上記のように不適切です。

ほかの方も言われているようにコンストラクタ内でそのようにTask.Runしても処理実行の終了を待機はしません。したい場合はこちらの質問と同様にすればいいと思いますが、本来やるべきでない実装です。
コンストラクタでの非同期メソッドの実行
またTask.Run内でそのままObservableCollectionに対し編集を行った場合、UIスレッド以外からの編集になりますのでViewがバインディングしている場合は落ちます。間にBeginInvokeOnMainThreadを挟む必要があります。

またそのコードではTimelineに別のObservableCollectionを代入していますが、それではView側はそれを検知はしません。INotifyPropertyChangedのメソッドを使ってTimelineが変わったとNotifyする必要があります。ただしおそらくこのコレクションの入れ替えは上に書いたように本来の意図とは違うのではないかと思いますが。

どこでMainPageViewModelをMainPageのBindingContextにセットされているのかわかりませんが、おそらくTask.Runの実行が終わる前にBindingContextにセットされる->TimelineはまだnullなのでBindingされない->Task.Runの実行が終わってTimelineにセットされたがMainPage側はそれを知る由もない、となっているのではとエスパーします。

投稿 2017/07/10 15:01

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    以下のような回答は評価を下げられます

    • 間違っている回答
    • 質問の回答になっていない投稿
    • 不快な投稿

    評価を下げる際はその理由をコメントに書き込んでください。

15分調べてもわからないことは、teratailで質問しよう!

ただいまの回答率

91.78%

関連した質問

同じタグがついた質問を見る

  • C#

    3900questions

    C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

  • Xamarin

    254questions

    Xamarin(ザマリン)は、iPhoneなどのiOSやAndroidで動作し、C# 言語を用いてアプリを開発できるクロスプラットフォーム開発環境です。Xamarin Studioと C# 言語を用いて、 iOS と Android の両方の開発を行うことができます。

  • MVVM

    46questions

    MVVM(Model View ViewModel)は構築上のデザインパターンで、表現ロジック(ViewModel)によってデータ(Model)からページ(View)を分離させます。

閲覧数の多いC#の質問

  • トップ
  • C#に関する質問
  • 【C#】【MVVM】非同期でObservableCollectionに追加したデータをViewに反映させる方法が知りたい