WP7 - Toevoegen aan collectie over dispatcher

Ik download een XML-bestand van een Twitter-feed op de achtergrond met behulp van HttpWebRequest en Deployment.Current.Dispatcher.BeginInvoke() om de resultaten te verwerken en in te stellen als de ItemsSource van mijn ListBox . Wat ik nu wil doen, is de xml verzamelen van meerdere twitterfeeds, deze combineren in een verzameling en deze toewijzen aan de eigenschap ItemsSource .

Ik dacht dat ik een teller in de klas zou gebruiken en een verzameling in de klas, en deze beide bijwerken elke keer dat een aanvraag wordt voltooid en wanneer de teller het aantal feeds (7) raakt, de ItemsSource overeenkomstig. Het probleem is, ik ben erg nieuw voor C #/WP7 en ondervind hier wat problemen. Dit is wat ik nu aan het doen ben, wat duidelijk niet klopt, omdat welk verzoek dan ook het laatst de ItemsSoure overschrijft, EN, ik weet niet zeker hoe ik ze in een 'globale' container moet steken, omdat het lijkt zoals de Dispatcher heeft een ander bereik:

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
Ik denk dat elke eenvoudige gegevensstructuur zoals List of ObservableCollection kan worden bijgewerkt "in Dispatchers" of van een achtergrondthread, het probleem is dat als het gebonden is aan een UI-element - het UI-element misschien niet bevalt van toegang vanaf een achtergrondthread, dus je zou alleen je gebonden collecties van achtergrondthreads moeten bijwerken met behulp van Dispatcher.Invoke (() => updatecall);
toegevoegd de auteur Filip Skakun, de bron

2 antwoord

Wat u moet doen, is om ergens in uw weergave een persistent, te koppelen collectievoorwerp te maken en dat een keer aan ItemsSource toe te wijzen. Als nieuwe items worden ontvangen, voegt u ze gewoon toe aan de verzameling.

U wilt een bestaand collectietype kiezen dat meldingen ondersteunt wanneer de verzameling wordt gewijzigd of u kunt er zelf een implementeren. In Silverlight denk ik dat je beste (enige?) Inzet het System.Collections.ObjectModel.ObservableCollection is. De andere optie is om System.Collections.Specialized.INotifyCollectionChanged te implementeren in een aangepaste klasse die erft van een ander type verzameling.

Wat betreft het sorteren, als uw aankomende gegevens altijd later zijn dan uw bestaande gegevens, kunt u de nieuwe items sorteren voordat u ze aan de verzameling toevoegt (u wilt ze misschien vooraan in de verzameling invoegen als u ze wilt weergeven ze met de nieuwste bovenaan).

Als de volledige verzameling echter moet worden gesorteerd telkens wanneer nieuwe records worden toegevoegd, moet u System.ICcomparable in uw artikelklasse implementeren. In dit geval zou ik de volgende aanpak aanbevelen:

Maak een nieuwe collectieklasse op basis van System.Collection.Generic.List (deze bevat een native Sort() -methode).

Implementeer INotifyCollectionChanged in deze klasse en verhoog de CollectionChanged -gebeurtenis met de actie NotifyCollectionChangedAction.Reset nadat uw records zijn toegevoegd en gesorteerd.

Implementeer ICvergelijkbaar in uw artikelklasse om de items gesorteerd volgens uw regels te krijgen.

Update met implementatie van INotifyCollectionChanged

Het grootste deel van de suggestie is vrij eenvoudig, maar de implementatie van de INotifyCollectionChanged is een beetje lastig, dus ik neem het hier op:

 _
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

Om dit evenement te verhogen, zodat consumenten op de hoogte zijn van wijzigingen in de lijst:

Call RaiseListChangedEvent(New NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset))
2
toegevoegd
Klinkt moeilijk (maar leuk)! Ik neem aan dat als ik door MSDN kijk, ik weet wat ik moet weten om zoiets te doen? Zo nee, heb je goede tutorials/koppelingen voor algemene Meldingsimplementaties enzovoort?
toegevoegd de auteur Josh, de bron
Geen probleem, ik waardeer echt alles wat je me hebt gegeven. Ik ben een professionele ontwikkelaar, maar ik heb nu slechts ongeveer 5 dagen MS/C # -codering gedaan !!!
toegevoegd de auteur Josh, de bron
De INotifyCollectionChanged-implementatie is het meest uitdagend, maar ik heb mijn oplossing in het antwoord opgenomen. De rest zou gemakkelijk vindbaar moeten zijn (sorry, ik heb geen specifieke links sinds we dit vorig jaar ergens hebben geïmplementeerd).
toegevoegd de auteur competent_tech, de bron
Sorry, ik vergat dat dit een C# -vraag was; de code die ik heb toegevoegd was VB, maar er zouden vertalingen moeten zijn. De enige belangrijke opmerking is dat als de itemklasse geserialiseerd is, je de gebeurtenis er niet mee wilt serialiseren, omdat het technisch gezien een serialiseerbaar lid is, wat ik nooit echt heb begrepen.
toegevoegd de auteur competent_tech, de bron

Een eenvoudige oplossing zou zijn om een ​​ObservableCollection als uw Itemsource te gebruiken:

TwitterListBox.ItemsSource = new ObservableCollection().

Wanneer u meer items ontvangt om toe te voegen - gewoon doen:

var itemsSource = (ObservableCollection)TwitterListBox.ItemsSource;

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

Als je deze gesorteerd wilt hebben, moet je itemsSource.Insert (twitterItem, i) doen nadat je hebt bedacht waar je het nieuwe item wilt invoegen. Er zijn waarschijnlijk een paar manieren om dat te doen, maar ervan uitgaande dat je je created_at als volgt deelt:

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

Dit is ongeveer hoe je het zou kunnen doen:

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++;
}

Of een betere oplossing:

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
toegevoegd