2017-05-21 2 views
-1

L'application prend le texte à partir d'inputField recherche dans la regex et renvoie les résultats dans outputField. Pour le mettre en quelques lignes de code que j'ai quelque chose comme ceci:C# WPF Freezing UI

public class Parser { 
    public Parser(string _text) 
    { 
     text = _text; 
    } 

    private string text { get; set; } 

    public string[] find() 
    { 

     string r1 = "..."; 
     string r2 = "..."; 
     string r3 = "..."; 

     string[] regArray = new string[] { r1, r2, r3 }; 

     List<string> resL = new List<string>(); 

     for (int i = 0; i < regArray.Length; i++) 
     { 
      MatchCollection matchList = Regex.Matches(text, regArray[i]); 
      var list = matchList.Cast<Match>().Select(match => match.Value).ToList(); 
      resL.AddRange(list); 
     } 

     string[] res = resL.Distinct().ToArray(); 
     if (res.Length > 0) 
      return res; 
     return new string[0]; 
    } } 

private async void FindButton_Click(object sender, RoutedEventArgs e) { 
    FindButton.Content = "Searching..."; 
    FindButton.IsEnabled = false; 
    await Task.Delay(1); 

    try 
    { 
     string parsed = string.Empty; 
     if (string.IsNullOrWhiteSpace(new TextRange(InputField.Document.ContentStart, InputField.Document.ContentEnd).Text)) ; 
     { 
      OutputField.Document.Blocks.Clear(); 
      MessageBox.Show("Empty input"); 
     } 
     else 
     { 
      Parser nOb = new Parser(new TextRange(InputField.Document.ContentStart, InputField.Document.ContentEnd).Text); 
      string[] result = nOb.find(); 

      if (result.Length == 0) 
      { 
       OutputField.Document.Blocks.Clear(); 
       MessageBox.Show("Nothing found"); 
      } 
      else 
      { 
       for (int i = 0; i < result.Length; i++) 
       { 
        parsed += result[i] + Environment.NewLine; 
       } 

       OutputField.Document.Blocks.Clear(); 
       OutputField.Document.Blocks.Add(new Paragraph(new Run(parsed))); 

       MessageBox.Show("Success"); 
      } 
     } 
    } 
    catch (Exception ex) 
    { 
     MessageBox.Show("Error: " + ex.Message); 
    } 

    FindButton.Content = "Default"; 
    FindButton.IsEnabled = true; 
} 

Le problème est que lorsque le texte de inputField est vraiment grand et le programme essaie de trouver tous les matches interface utilisateur commence à geler. Il devient impossible de minimiser la fenêtre du programme, Windows dit que l'application ne répond pas et demande si je veux le fermer. Si je ne clique pas sur le programme pendant le travail, ça finit bien. Alors, est-il possible d'éviter le gel en quelque sorte et permettre de minimiser l'application alors qu'il fonctionne avec de grandes entrées? Toute aide est appréciée.

+0

sans savoir ce que votre Task.Delay est pour nous ne pouvons pas vraiment aider. – Kelly

Répondre

1

Essayez d'exécuter la méthode find() sur un fil de fond:

private async void FindButton_Click(object sender, RoutedEventArgs e) 
{ 
    FindButton.Content = "Searching..."; 
    FindButton.IsEnabled = false; 

    try 
    { 
     if (string.IsNullOrWhiteSpace(new TextRange(InputField.Document.ContentStart, InputField.Document.ContentEnd).Text)) 
     { 
      OutputField.Document.Blocks.Clear(); 
      MessageBox.Show("Empty input"); 
     } 
     else 
     { 
      string[] result; 
      await Task.Run(() => 
      { 
       Parser nOb = new Parser(new TextRange(InputField.Document.ContentStart, InputField.Document.ContentEnd).Text); 
       result = nOb.find(); 
      }); 

      if (result == null || result.Length == 0) 
      { 
       OutputField.Document.Blocks.Clear(); 
       MessageBox.Show("Nothing found"); 
      } 
      else 
      { 
       StringBuilder sb = new StringBuilder(); 
       for (int i = 0; i < result.Length; i++) 
       { 
        sb.AppendLine(result[i]); 
       } 

       OutputField.Document.Blocks.Clear(); 
       OutputField.Document.Blocks.Add(new Paragraph(new Run(sb.ToString()))); 

       MessageBox.Show("Success"); 
      } 
     } 
    } 
    catch (Exception ex) 
    { 
     MessageBox.Show("Error: " + ex.Message); 
    } 

    FindButton.Content = "Default"; 
    FindButton.IsEnabled = true; 
} 
0

C'est le comportement attendu lorsque vous exécutez votre tâche sur le thread de l'interface utilisateur par défaut, et tant qu'il n'est pas terminé, aucune opération de l'interface utilisateur n'est possible.

Vous devez utiliser Task.Run pour que le code soit exécuté sur un thread non-UI. Mais vous devrez peut-être mettre à jour certains contrôles, alors vous devrez revenir au thread d'interface utilisateur et vous pourrez utiliser Dispatcher.RunAsync, bien qu'il soit préférable d'utiliser la liaison de données dans WPF.

Ce sont des choses assez basiques et vous devriez lire MSDN sur ces sujets.

1

C'est horrible:

for (int i = 0; i < result.Length; i++) 
{ 
    parsed += result[i] + Environment.NewLine; 
} 

Parce que les chaînes sont immuables, cela crée des chaînes N, chacune plus que la dernière, la consommation totale de mémoire O (N). Et le temps perdu à copier est également O (N).

Le mieux est d'utiliser String.Join, mais quand vous devez faire la manipulation de chaînes dans une boucle, utilisez System.Text.StringBuilder, qui modifie sa mémoire tampon interne au lieu de le jeter et faire une copie complète pour chaque opération, la façon string doit.

Mais, pourquoi faites-vous une chaîne multiligne en une seule fois en premier lieu? Probablement chacun d'entre eux devrait être un objet Paragraphe distinct, et laisser le conteneur WPF gérer l'écrêtage (et ne pas avoir à dessiner des objets Paragraphe complètement en dehors de l'écran). Cependant, si vous le faites, créez un objet de contenu déconnecté de l'interface utilisateur, puis ajoutez-le à l'interface utilisateur en une seule fois, afin que le moteur de mise en page ne se termine pas à chaque concaténation.

+0

Cela ne répond pas à la question. –

+3

@Erno: Choisir le bon algorithme est le seul moyen de garder l'ordinateur sensible. Si l'ensemble du système d'exploitation est lié par des nœuds échangés sur le disque, l'interface utilisateur du programme qui le fait ne répondra pas non plus, même si des threads d'arrière-plan sont utilisés. La collecte fréquente des ordures gèle également l'interface utilisateur. –

+2

@Erno: Bien sûr, si après la fixation du coût global de l'opération, il fonctionne encore assez longtemps pour que l'interface utilisateur se bloque, alors les tâches en arrière-plan doivent être utilisées. Mais il est préférable de régler le problème de performance plutôt que de le cacher. –

-1

Je dois admettre que je ne comprenais pas ce qui était le but de votre code, mais je voudrais essayer quelque chose comme ceci:

I simplifié votre classe Parser:

public class Parser 
    { 
     const string _r1 = "..."; 
     private string _text; 

     public Parser(string text) 
     { 
      _text = text; 
     } 

     public IEnumerable<string> Find() 
     { 
      return Regex.Matches(_text, _r1) 
       .Cast<Match>() 
       .Select(match => match.Value) 
       .Distinct(); 
     } 
    } 

Et une partie de vous bouton code de clic, en utilisant un StringBuilder:

Parser nOb = new Parser(yourText); 
var result = nOb.Find(); 
string parsed = string.Empty; 

if (!result.Any()) 
{ 
    // Nothing found 
} 
else 
{ 
    var stringBuilder = new StringBuilder(); 
    foreach (var line in result) 
    { 
     stringBuilder.AppendLine(line); 
    } 

    parsed = stringBuilder.ToString(); 

    // ...parsed 
} 

Si vous spécifiez mieux l'objectif final de votre code, nous pouvons aider plus précisément.