WP7 - إضافة إلى جمع عبر المرسل

أقوم بتنزيل xml من موجز Twitter في الخلفية ، باستخدام HttpWebRequest ، و Deployment.Current.Dispatcher.BeginInvoke() لمعالجة النتائج ، وتعيينهم ItemsSource الخاصة بي ListBox . ما أريد القيام به الآن ، هو جمع xml من خلاصات Twitter متعددة ، ودمجها في مجموعة ، وتعيين ذلك إلى الخاصية ItemsSource .

فكنت أحسب أنني سأستخدم عدادًا في الفصل ، ومجموعة في الفصل ، ونقوم بتحديث كل مرة يتم فيها إكمال الطلب ، وعندما يضرب العداد عدد الخلاصات (7) ، اضبط ItemsSource وفقا لذلك. المشكلة هي ، أنا جديد جدًا في C #/WP7 ، وأواجه بعض المشكلات هنا. هذا ما كنت أعمل به الآن ، وهذا خطأ واضح ، لأن أي طلب ينتهي ينتهي بالكتابة فوق ItemsSoure ، وأنا لست متأكداً من كيفية لصقها في حاوية "عالمية" ، لأنه يبدو مثل المرسل له نطاق مختلف:

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 قابلة للتحديث "عبر المرسلون" أو من مؤشر ترابط للخلفية ، فإن المشكلة هي أنه إذا كانت مرتبطة بعنصر واجهة المستخدم - فقد لا يرغب عنصر واجهة المستخدم في الوصول إليها من مؤشر ترابط الخلفية ، لذلك يجب فقط تحديث المجموعات المرتبطة من مؤشرات الترابط الخلفية باستخدام Dispatcher.Invoke (() => updatecall)؛
وأضاف المؤلف Filip Skakun, مصدر

2 إجابة

ما عليك القيام به هو إنشاء كائن مجموعة ثابت ، يمكن ربطه في مكان ما في العرض الخاص بك وتعيين ذلك إلى ItemsSource مرة واحدة. عند استلام عناصر جديدة ، ما عليك سوى إضافتها إلى المجموعة.

ستحتاج إلى اختيار نوع مجموعة موجود يدعم الإشعارات عند تعديل المجموعة أو تنفيذ واحدة بنفسك. في Silverlight ، أعتقد أن أفضل رهان (فقط؟) هو System.Collections.ObjectModel.ObservableCollection . الخيار الآخر هو تطبيق System.Collections.Specialized.INotifyCollectionChanged في فئة مخصصة ترث من نوع مختلف من مجموعة.

بالنسبة للفرز ، إذا كانت بياناتك القادمة ستكون دائمًا متأخرة عن بياناتك الحالية ، فيمكنك فرز العناصر الجديدة قبل إضافتها إلى المجموعة (قد ترغب في إدراجها في الجزء الأمامي من المجموعة إذا كنت ترغب في عرض لهم مع الأحدث في الأعلى).

ومع ذلك ، إذا كانت المجموعة بأكملها بحاجة إلى الفرز في كل مرة يتم فيها إضافة سجلات جديدة ، فستحتاج إلى تنفيذ System.IComparable على فئة العنصر الخاص بك. في هذه الحالة ، أوصي بالنهج التالي:

إنشاء فئة مجموعة جديدة استناداً إلى System.Collection.Generic.List (يحتوي هذا على أسلوب Sort() أصلي).

تطبيق INotifyCollectionChanged في هذه الفئة ورفع الحدث CollectionChanged مع الإجراء NotifyCollectionChangedAction.Reset بعد إضافة السجلات وفرزها.

قم بتطبيق 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 ، ولكن يجب أن تكون هناك ترجمات هناك. ملاحظة مهمة واحدة هي أنه في حالة تسلسل فئة العنصر ، لا تريد إجراء تسلسل للحدث معه لأنه عضو تقني قابل للتسلسل ، وهو أمر لم أفهمه أبدًا.
وأضاف المؤلف competent_tech, مصدر

الحل ببساطة هو استخدام ObservableCollection كمصدر العناصر الخاص بك:

TwitterListBox.ItemsSource = new ObservableCollection().

كلما تلقيت المزيد من العناصر لإضافتها - قم بما يلي:

var itemsSource = (ObservableCollection)TwitterListBox.ItemsSource;

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

إذا كنت تريد فرز هذه العناصر - تحتاج إلى تنفيذ العناصر Source.Insert (twitterItem، i) بعد معرفة مكان إدراج العنصر الجديد. قد تكون هناك بعض الطرق للقيام بذلك ، ولكن على افتراض أنك قمت بتحليل الكلمات التي أنشأتها مثل:

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
وأضاف