WP7 - コレクションに追加ディスパッチャ間

私は、 HttpWebRequestDeployment.Current.Dispatcher.BeginInvoke()を使用してバックグラウンドでTwitterフィードからXMLをダウンロードし、結果を処理して私の ListBoxItemsSource 私が今やりたいことは、複数のtwitterフィードからXMLを集め、コレクションにまとめ、 ItemsSource プロパティに割り当てます。

私は、クラス内のカウンタとクラス内のコレクションを使用し、要求が完了するたびにカウンタを更新し、カウンタがフィードカウント(7)に達すると、 ItemsSource それに応じて。問題は、私はC#/ WP7の新機能で、ここにいくつかの問題があることです。これは私が今作業しているものですが、明らかに間違っています。要求が最後に終了してを上書きしてしまい、「グローバル」コンテナに貼り付ける方法がわからないためですディスパッチャのように異なるスコープがあります:

string[] feeds = { "badreligion",
                "DoctorGraffin",
                "BrettGurewitz",
                "jay_bentley",
                "brtour",
                "GregHetson",
                "theBRpage" };

// invoked in the constructor
private void StartTwitterUpdate()
{
    foreach (string feed in feeds)
    {
        HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(new Uri("http://api.twitter.com/1/statuses/user_timeline.xml?screen_name=" + feed));

        request.BeginGetResponse(new AsyncCallback(twitter_DownloadStringCompleted), request);
    }
}

// the AsyncCallback
void twitter_DownloadStringCompleted(IAsyncResult asynchronousResult)
{
    HttpWebRequest request = (HttpWebRequest)asynchronousResult.AsyncState;

    HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(asynchronousResult);

    using (StreamReader streamReader1 =
        new StreamReader(response.GetResponseStream()))
    {
        string resultString = streamReader1.ReadToEnd();

        XElement xmlTweets = XElement.Parse(resultString);

       //here I need to add to a collection, and if the max is hit, set the ItemsSource
        Deployment.Current.Dispatcher.BeginInvoke(() =>
        {
            TwitterListBox.ItemsSource = from tweet in xmlTweets.Descendants("status")
                                            select new TwitterItem
                                            {
                                                CreatedAt = tweet.Element("created_at").Value,
                                                Id = tweet.Element("id").Value,
                                                ImageSource = tweet.Element("user").Element("profile_image_url").Value,
                                                Message = tweet.Element("text").Value,
                                                UserName = "@" + tweet.Element("user").Element("screen_name").Value
                                            };
        });
    }
}

EDIT Also, if it matters, I'll have to sort the final collection before sending it to ItemsSource, via TwitterItem.CreatedAt, so if someone could suggest an optimal data structure for sorting and easy ItemsSource assignment, that'd be great!

1
私はList やObservableCollection のような単純なデータ構造はDispatchers間やバックグラウンドスレッド間で更新可能だと思うのですが、UI要素にバインドされていればUI要素はバックグラウンドスレッドであるため、Dispatcher.Invoke(()=> updatecall)を使用してバックグラウンドスレッドからバインドされたコレクションのみを更新する必要があります。
追加された 著者 Filip Skakun,

2 答え

必要なのは、永続的なバインド可能なコレクションオブジェクトをビューのどこかに作成し、そのオブジェクトをItemsSourceに一度割り当てることです。新しいアイテムが届くと、それらをコレクションに追加するだけです。

コレクションが変更されたときに通知をサポートする既存のコレクションタイプを選択するか、自分でコレクションタイプを実装するかのどちらかです。 Silverlightでは、あなたのベスト(唯一の)ベットは System.Collections.ObjectModel.ObservableCollection だと思います。もう1つのオプションは、異なるタイプのCollectionから継承するカスタムクラスに System.Collections.Specialized.INotifyCollectionChanged を実装することです。

並べ替えに関しては、到着するデータが常に既存のデータよりも遅れている場合、新しいアイテムをコレクションに追加する前に並べ替えることができます(コレクションの前に挿入することもできます彼らはトップに最新のものを持っています)。

ただし、新しいレコードが追加されるたびにコレクション全体をソートする必要がある場合は、アイテムクラスに System.IComparable を実装する必要があります。この場合、次の方法をお勧めします。

System.Collection.Generic.List (これにはネイティブのSort()メソッドが含まれています)に基づいて新しいコレクションクラスを作成します。

このクラスに INotifyCollectionChanged を実装し、レコードが追加およびソートされた後に NotifyCollectionChangedAction.Reset アクションで CollectionChanged イベントを発生させます。

アイテムクラスに IComparable を実装して、ルールに従ってソートされたアイテムを取得します。

INotifyCollectionChangedの実装による更新

ほとんどの提案はかなり単純ですが、INotifyCollectionChangedの実装はややこしいので、ここに含めてみましょう:

 _
Private m_ListChangedEvent As NotifyCollectionChangedEventHandler

''' 
''' This event is raised whenever the list is changed and implements the IBindingList ListChanged event '''
 
''' 
''' 
''' 
Public Custom Event ListChanged As NotifyCollectionChangedEventHandler Implements INotifyCollectionChanged.CollectionChanged
     _
    AddHandler(ByVal value As NotifyCollectionChangedEventHandler)
        m_ListChangedEvent = DirectCast([Delegate].Combine(m_ListChangedEvent, value), NotifyCollectionChangedEventHandler)
    End AddHandler

     _
    RemoveHandler(ByVal value As NotifyCollectionChangedEventHandler)
        m_ListChangedEvent = DirectCast([Delegate].Remove(m_ListChangedEvent, value), NotifyCollectionChangedEventHandler)
    End RemoveHandler

    RaiseEvent(ByVal sender As Object, ByVal e As NotifyCollectionChangedEventArgs)
        If m_ListChangedEvent IsNot Nothing Then
            m_ListChangedEvent.Invoke(sender, e)
        End If
    End RaiseEvent
End Event

消費者がリストの変更を認識できるようにこのイベントを発生させるには:

Call RaiseListChangedEvent(New NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset))
2
追加された
難しいですが(楽しいですね)!私はMSDNを見て、私がそのようなことをするために知る必要があるものを私に与えるだろうと仮定しています。そうでない場合は、一般的な通知実装などのための良いチュートリアル/リンクがありますか?
追加された 著者 Josh,
問題はない、私は本当にあなたが私に与えてくれたことに感謝します。私はプロの開発者ですが、MS/C#コーディングは約5日間行っています!
追加された 著者 Josh,
INotifyCollectionChangedの実装は最も難しいですが、私は答えに私のソリューションを含めました。残りの部分は簡単に発見できるはずです(申し訳ありませんが、昨年、いつか実装して以来、具体的なリンクはありません)。
追加された 著者 competent_tech,
申し訳ありませんが、これはC#の質問でした。私が追加したコードはVBでしたが、そこに翻訳があるはずです。 1つの重要な注意点は、アイテムクラスがシリアライズされている場合、それは技術的にシリアライズ可能なメンバーなので、シリアライズしたくないということです。
追加された 著者 competent_tech,

簡単な解決策は、ObservableCollectionをItemsSourceとして使用することです。

TwitterListBox.ItemsSource = new ObservableCollection().

追加するアイテムが増えた場合は、

var itemsSource = (ObservableCollection)TwitterListBox.ItemsSource;

foreach(var twitterItem in newTweets)
{
    itemsSource.Add(twitterItem);
}

これらをソートしたい場合は、新しいアイテムを挿入する場所を特定した後itemsSource.Insert(twitterItem、i)を実行する必要があります。おそらくそれを行うにはいくつかの方法がありますが、created_atを次のように解析すると仮定します:

CreatedAt = DateTime.ParseExact(createdAt, "ddd MMM dd HH:mm:ss zzz yyyy", CultureInfo.InvariantCulture)

これはおおまかなやり方です:

int i = 0;//insert index
int j = 0;//new items index

while (j < newTweets.Count)
{
    while (i < itemsSource.Count &&
        itemsSource[i].CreatedAt >= newTweets[j].CreatedAt)
    {
        i++;
    }

    itemsSource.Insert(i, newTweets[j]);
    j++;
}

または、より洗練されたソリューション

public partial class MainPage : PhoneApplicationPage
{
   //Constructor
    public MainPage()
    {
        InitializeComponent();

        var itemsSource = new ObservableCollection();
        var initialTweets = new[]
                                {
                                    new TwitterItem
                                        {CreatedAt = DateTime.Now.AddMinutes(-3)},
                                    new TwitterItem
                                        {CreatedAt = DateTime.Now.AddMinutes(-2)},
                                    new TwitterItem
                                        {CreatedAt = DateTime.Now.AddMinutes(-1)}
                                };
        itemsSource.Merge(initialTweets.OrderByDescending(ti => ti.CreatedAt));

        var newTweets = new List();
        newTweets.Add(new TwitterItem {CreatedAt = DateTime.Now.AddMinutes(-3.5)});
        newTweets.Add(new TwitterItem {CreatedAt = DateTime.Now.AddMinutes(-2.5)});
        newTweets.Add(new TwitterItem {CreatedAt = DateTime.Now.AddMinutes(-1.5)});
        newTweets.Add(new TwitterItem {CreatedAt = DateTime.Now.AddMinutes(-0.5)});
        itemsSource.Merge(newTweets.OrderByDescending(ti => ti.CreatedAt));

        foreach (var twitterItem in itemsSource)
        {
            Debug.WriteLine(twitterItem.CreatedAt.ToString());
        }
    }
}

public class TwitterItem
{
    public DateTime CreatedAt;
}

public static class ObservableTwitterItemsExtensions
{
    public static void Merge(
        this ObservableCollection target, IEnumerable source)
    {
        int i = 0;//insert index

        foreach (var newTwitterItem in source)
        {
            while (i < target.Count &&
                target[i].CreatedAt >= newTwitterItem.CreatedAt)
            {
                i++;
            }

            target.Insert(i, newTwitterItem);
        }
    }
}
1
追加された