2010-08-02 15 views
6

Beh, ho provato diversi metodi per far funzionare tutto questo, l'operatore in background, Dispatcher.Invoke, il threading all'interno della classe chiamata e nulla sembra funzionare. La soluzione migliore finora è un metodo di estensione che chiama il richiamo del controllo. Inoltre, ho cercato di evitare di passare i dati per l'etichetta attraverso le mie classi di eventi e semplicemente invocando il mio codice di elaborazione, tuttavia ciò non ha fatto alcuna differenza.WPF - Aggiornamento del contenuto dell'etichetta durante l'elaborazione

Riguardo al componente di sfondo, ho continuato a ottenere eccezioni dicendo che lo sfondo era occupato, quindi ho istanziato la classe diverse volte, tuttavia l'etichetta cambiava solo visibilmente una volta completata l'intera operazione.

Ho rimosso il mio codice precedente, ecco tutto ciò che è rilevante, in quanto sembra che il problema sia difficile da risolvere.

Metodo essere chiamato

private void TestUris() 
     { 
      string text = new TextRange(rtxturis.Document.ContentStart, rtxturis.Document.ContentEnd).Text; 
      string[] lines = Regex.Split(text.Remove(text.Length - 2), "\r\n"); 

      foreach (string uri in lines) 
      { 
       SafeUpdateStatusText(uri); 
       bool result; 
       string modUri; 

       if (!uri.Contains("http://")) 
       { 
        modUri = uri; 
        result = StoreData.LinkUriExists(new Uri("http://" + modUri)); 
       } 
       else 
       { 

        modUri = uri.Substring(7); 
        result = StoreData.LinkUriExists(new Uri(uri)); 
       } 

       if (!result) 
       { 
        Yahoo yahoo = new Yahoo(); 
        yahoo.Status.Sending += (StatusChange); 
        uint yahooResult = 0; 

        yahooResult = yahoo.ReturnLinkCount(modUri); 

        if (yahooResult > 1000) 
        { results.Add(new ScrapeDetails(Guid.NewGuid(), modUri, 1000, "Will be processed", true)); } 
        else 
        { results.Add(new ScrapeDetails(Guid.NewGuid(), modUri, (int)yahooResult, "Insufficient backlinks", false)); } 

       } 
       else 
       { 
        results.Add(new ScrapeDetails(Guid.NewGuid(), modUri, 0, "Previously been processed", false)); 
       } 
      } 


      foreach (var record in results) 
      { 
       dgvresults.Items.Add(record); 

      } 

      EnableStartButton(); 

     } 

Yahoo Classe

public class Yahoo 
    {   

     /// <summary> 
     /// Returns the amount of links each Uri has. 
     /// </summary> 
     public uint ReturnLinkCount(string uri) 
     { 
      string html; 
      Status.Update(uri, false); //this is where the status is called 
      try 
      { 

       html = client.DownloadString(string.Format("http://siteexplorer.search.yahoo.com/search?p=http%3A%2F%2F{0}&fr=sfp&bwm=i", uri)); 

      } 
      catch (WebException ex) 
      { 
       ProcessError(ex.ToString()); 
       return 0; 
      } 

      return (LinkNumber(html)); 

     } 

Classi di stato

public class StatusEventArgs : EventArgs 
    { 
     private string _message; 
     private bool _isidle; 

     public StatusEventArgs(string message, bool isidle) 
     { 
      this._message = message; 
      this._isidle = isidle; 
     } 

     public bool IsIdle 
     { 
      get { return _isidle; } 
     } 

     public string Message 
     { 
      get { return _message; } 
     } 
    } 

    public class Status 
    { 
     public Status() 
     { 
     } 

     // Declaring an event, with a custom event arguments class 
     public event EventHandler<StatusEventArgs> Sending; 

     // Some method to fire the event. 
     public void Update(string message, bool isIdle) 
     { 
      StatusEventArgs msg = new StatusEventArgs(message, isIdle); 
      OnUpdate(msg); 
     } 

     // The method that invokes the event. 
     protected virtual void OnUpdate(StatusEventArgs e) 
     { 
      EventHandler<StatusEventArgs> handler = Sending; 

      if (handler != null) 
      { 
       handler(this, e); 
      } 
     } 
    } 

metodo che modifica le etichette Contenuto

private void StatusChange(object sender, StatusEventArgs e) 
     { 

      if(!e.IsIdle) 
      { 
       lblstatus.Content = e.Message; 
       lblstatus.Foreground = StatusColors.Green; 
       lblstatus.Refresh(); 
      } 
      else 
      { 
       lblstatus.Content = e.Message; 
       lblstatus.Foreground = StatusColors.Grey; 
       lblstatus.Refresh(); 
      } 

     } 

Il metodo statico Refresh chiamato:

public static class ExtensionMethods 
    { 
     private static Action EmptyDelegate = delegate() { }; 

     public static void Refresh(this UIElement uiElement) 
     { 
      uiElement.Dispatcher.Invoke(DispatcherPriority.Render , EmptyDelegate); 
     } 

Un altro EDIT: Fissando il mio codice per un po 'di più, mi sono reso conto, che il foreach loop verrà eseguito molto rapidamente, l'operazione che richiede tempo, è

yahooResult = yahoo.ReturnLinkCount(modUri); 

Pertanto ho dichiarato edita la classe di stato (che gestisce l'evento e invoca l'etichetta, ecc.) e vi si iscrive. Ho ottenuto risultati migliori, anche se mi sembrano ancora casuali, a volte vedo un paio di aggiornamenti delle etichette e, a volte, anche se viene passato lo stesso identico URI, così strano.

+0

È questo il codice in esecuzione all'interno di un thread diverso? – decyclone

+0

No, purtroppo non sono ancora riuscito a risolverlo ancora :(. – Ash

risposta

3

risolto SI WOOHOOOOOOOO 3 giorni di test, test, test.

Ho deciso di avviare un nuovo progetto solo con il metodo di estensione sopra e un semplice ciclo per verificare la funzionalità di aggiornamento dell'interfaccia utente. Ho iniziato a testare DispatchPrioraties diversi (li ho testati tutti).

Stranamente, ho trovato che le priorità più alte erano le peggiori, ad esempio utilizzando Send non ha aggiornato l'etichetta, Render up lo ha aggiornato due volte in media. Questo è stato il comportamento strano che stavo vivendo mentre provavo diverse priorità. Ho scoperto Background:

Il valore di enumerazione è 4. Le operazioni vengono elaborate dopo che tutte le altre operazioni non inattive sono state completate.

Ora questo suona esattamente quello che non volevo, poiché ovviamente l'etichetta dovrebbe essere aggiornata durante l'elaborazione, quindi perché non l'ho mai provato. Suppongo che una volta che uno dei miei metodi sia stato completato, prima che venga chiamato il prossimo, l'interfaccia utente verrà aggiornata. Sono di indovinare, ma il 100% si aggiorna costantemente in modo corretto su due operazioni separate.

Grazie a tutti.

+0

sono così confuso .... mi sembra di avere lo stesso problema, e non riesco a capirlo ... solo gli aggiornamenti alla fine del metodo invece ... quando la linea viene colpita nel debugger ... per favore assistete. – Seabizkit

0

sarebbe più facile/migliore aggiungere le informazioni di stato come una proprietà su questo oggetto e fare in modo che vengano attivate le notifiche di modifica delle proprietà?

in questo modo il testo dell'etichetta (o qualsiasi altra cosa) potrebbe essere associato alla proprietà anziché avere il lavoro asincrono che tenta di aggiornare un'etichetta?

oppure aggiungere un metodo come questo per aggiornare lo stato se è necessario aggiornarlo?

void SafeUpdateStatusText(string text) 
    { 
     // update status text on event thread if necessary 
     Dispatcher.BeginInvoke(DispatcherPriority.Background, (SendOrPostCallback)delegate 
     { 
      lblstatus.Content = text; 
     }, null); 
    } 

in caso contrario, non credo che abbiamo abbastanza informazioni per aiutare ancora ....

+0

Grazie per il metodo e il consiglio John. Ho provato questo metodo cinque volte con diversi preventivi di Dispatch e l'etichetta non cambia fino al metodo che ho postato sopra è completo, quindi visualizza l'ultimo uri elaborato – Ash

+0

Sembra così casuale, a volte mostra un aggiornamento, a volte di più, così strano considerando che sto passando gli stessi dati e anche le condizioni sono identiche – Ash

+0

lo stai facendo in un altro thread? potresti inserire delle interruzioni e assicurarti che l'interfaccia utente venga aggiornata correttamente. Se non lo stai firmando in un'altra discussione, potresti non fornire mai l'ora dell'interfaccia utente per cambiare? –

5

Spero che ci sia sth. utile ...

private void button1_Click(object sender, RoutedEventArgs e) 
{ 
    ThreadPool.QueueUserWorkItem(o => 
    { 
     int result = 0; 
     for (int i = 0; i < 9999999; i++) 
     { 
      result++; 
      Dispatcher.BeginInvoke(new Action(() => 
      { 
       this.label1.Content = result; 
      })); 
      Thread.Sleep(1); 
     } 
    }); 
} 
+0

Ho provato a mettere questo in un metodo e chiamarlo nella mia app WPF, ma l'etichetta non si aggiorna mai. Così ho fatto ricorso all'utilizzo del metodo Forms.Application.DoEvents(). Qualcuno può dirmi perché il mio metodo non riesce: SetStatus private void (stato string) { ThreadPool.QueueUserWorkItem (o => { Dispatcher.BeginInvoke (nuova azione (() => { lblStatus.Contenuto = stato; })); }); } –

3

Beh questo sta per sembrare stupido, ma si può solo fare riferimento allo spazio dei nomi forme, e allora si può fare questo

 using System.Windows.Forms; 

    mylabel = "Start"; 
    Application.doEvents(); 

    myLabel = "update" 
    Application.doEvents(); 

ora il problema utilizzando questo sarebbe si utilizza wpf ma puoi comunque fare riferimento ai moduli e usare questo spazio dei nomi. L'altro problema è ciò che è sempre nella coda che verrebbe eseguito direttamente sull'interfaccia utente. Tuttavia questo è il modo più semplice di fare gli aggiornamenti delle etichette che potrei pensare. Non sono sicuro di altre ragioni per cui questo sarebbe male da usare, ma è una soluzione per OP. Se hai una buona ragione per non sbagliare, basta informare me stesso e gli altri postando un commento. Grazie.

+0

funziona solo quando lo faccio, qual è il modo corretto di farlo? – Seabizkit

0

Spero che questo aiuta:

private delegate void UpdateLabelDelegate(DependencyProperty dp, object value); 

public void UpdateLabelContent(Label label, string newContent) 
{ 
    Dispatcher.Invoke(new UpdateLabelDelegate(label.SetValue), DispatcherPriority.Background, ContentProperty, newContent); 
} 

Usage:

while (true) 
{ 
    UpdateLabelContent(this.lblStatus, "Next random number: " + new Random().Next()); 
    Thread.Sleep(1000); 
} 
+0

perché non dovrebbe funzionare se chiamato da MainWindow: Window, qualsiasi idea, solo aggiornamento dopo che il metodo completo è terminato piuttosto che wen la riga viene eseguita. – Seabizkit

Problemi correlati