2010-08-02 16 views
6

Bueno, he intentado varios métodos para hacer que esto funcione, el trabajador de fondo, Dispatcher.Invoke, el enrutamiento dentro de la clase llamada y nada parece funcionar. La mejor solución hasta el momento es un método de extensión que llama a la invocación del control. También intenté evitar pasar los datos de la etiqueta a través de mis clases de eventos y simplemente invocando dentro de mi código de procesamiento, sin embargo, esto no hizo ninguna diferencia.WPF - Actualización del contenido de la etiqueta durante el procesamiento

En cuanto al componente de fondo, recibí excepciones diciendo que el trabajador de fondo estaba ocupado, así que instalé la clase varias veces, sin embargo, la etiqueta solo cambió visible una vez que se completó la operación.

He eliminado mi código anterior, aquí hay todo lo que es relevante, ya que parece que el problema es difícil de resolver.

Método Ser llamado

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 Clase

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)); 

     } 

Clases de estado

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

método que cambia las etiquetas de contenido

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(); 
      } 

     } 

El método estático denominado Actualizar:

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

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

otra edición: El mirar fijamente mi código para un poco más de tiempo, me he dado cuenta, que el foreach el ciclo se ejecutará realmente rápido, la operación que toma el tiempo, es

yahooResult = yahoo.ReturnLinkCount(modUri); 

Por lo tanto, he declar ed la clase de estado (que maneja el evento e invoca la etiqueta, etc.) y se le suscribe. Obtuve mejores resultados, aunque todavía se siente al azar, a veces veo un par de actualizaciones de etiquetas, y a veces uno a pesar de que se pasan los mismos URI, tan extraño.

+0

¿Es este código que se ejecuta dentro de un subproceso diferente? – decyclone

+0

No, lamentablemente todavía no he podido arreglarlo :(. – Ash

Respuesta

3

que resolvía SÍ WOOHOOOOOOOO 3 días de pruebas, pruebas, pruebas.

Decidí comenzar un nuevo proyecto solo con el método de extensión anterior y un bucle simple para probar la funcionalidad de actualización de la interfaz de usuario. Empecé probando diferentes DispatchPrioraties (los probé a todos).

Extrañamente, encontré que las prioridades más altas eran las peores, por ejemplo, al usar Send no actualizaba la etiqueta en absoluto, Render la actualizó dos veces en promedio. Este fue el comportamiento extraño que estaba experimentando cuando probé diferentes prioridades. Descubrí Background:

El valor de enumeración es 4. Las operaciones se procesan después de que se completen todas las demás operaciones no inactivas.

Ahora sonaba exactamente lo que no quería, ya que obviamente la etiqueta debería actualizarse durante el procesamiento, por lo que nunca lo intenté. Supongo que una vez que se ha completado uno de mis métodos, antes de la próxima llamada, la interfaz de usuario se actualiza. Me parece adivinar, pero se actualiza de forma coherente al 100% correctamente en dos operaciones separadas.

Gracias a todos.

+0

estoy tan confundido ... parece que estoy experimentando el mismo problema, y ​​no puedo entenderlo ... solo actualizaciones al final del método en su lugar ... cuando se golpea la línea en el depurador ... por favor ayuda. – Seabizkit

0

¿Sería más fácil/mejor agregar la información de estado como propiedad de este objeto y hacer que solo active las notificaciones de cambio de propiedad?

de esa manera, ¿el texto de la etiqueta (o lo que sea) podría vincularse a la propiedad en lugar de tener que trabajar con la función asíncrona para actualizar una etiqueta?

o agregue un método como este para actualizar el estado si tiene que actualizarlo?

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

de otro modo, no creo que tenemos suficientes datos para ayudar aún ....

+0

Gracias por el método y el consejo John. He probado ese método cinco veces con diferentes prioridades de despacho y la etiqueta no cambia hasta que el método que publiqué arriba está completo, luego muestra el último uri procesado. – Ash

+0

Se siente tan aleatorio, a veces muestra una actualización, a veces más, tan raro considerando que estoy pasando los mismos datos y las condiciones también son idénticas. – Ash

+0

lo haces en otro hilo? podría poner interrupciones y asegurarse de que su interfaz de usuario se está actualizando correctamente. Si no está en otro hilo, es posible que nunca le esté dando a la interfaz de usuario tiempo para cambiar? –

5

Espero que haya algo. útil ...

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

Intenté poner esto en un método y llamarlo a mi aplicación WPF, pero la etiqueta todavía no se actualiza. Así que recurrí al uso del método Forms.Application.DoEvents(). ¿Puede alguien decirme por qué mi método falla: SetStatus private void (string status) { ThreadPool.QueueUserWorkItem (o => { Dispatcher.BeginInvoke (nueva Acción (() => { lblStatus.Contenido = estado; })); }); } –

3

Bueno, esto va a sonar estúpido, pero sólo podría hacer referencia al espacio de nombres de formas, y entonces usted puede hacer esto

 using System.Windows.Forms; 

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

    myLabel = "update" 
    Application.doEvents(); 

ahora el problema de utilizar este estaría usted está utilizando wpf pero aún puede hacer referencia a formularios y usar este espacio de nombres. El otro problema es qué hay en la cola que se ejecutará directamente en la interfaz de usuario. Sin embargo, esta es la forma más simplista de hacer actualizaciones de etiquetas que podría pensar. No estoy seguro de otra razón de por qué esto sería malo de usar, pero es una solución para OP. Si tiene alguna buena razón, es incorrecto no votar, solo infórmeme a mí mismo y a otros publicando un comentario. Gracias.

+0

solo funciona cuando hago esto, ¿cuál es la forma correcta de hacerlo? – Seabizkit

0

Espero que esto ayude:

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

Uso:

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

por qué esto no funcionaría si se llama desde MainWindow: Window, cualquier idea, solo actualice después de que el método completo esté terminado en vez de que se ejecute la línea. – Seabizkit

Cuestiones relacionadas