2016-06-21 9 views
1

Ich habe eine einfache Konsolenanwendung, die den Fortschritt der Standardausgabe meldet. Zum Beispiel:Wie aktualisiere ich ein WPF-Steuerelement basierend auf der Konsolenausgabe eines Prozesses?

static void Main(string[] args) 
{ 
    for (int i = 0; i <= 100; i += 10) 
    { 
     Console.WriteLine(i); 
     System.Threading.Thread.Sleep(1000); 
    } 
} 

die diese Ausgabe erzeugt:

0 
10 
20 
30 
40 
50 
60 
70 
80 
90 
100 

möchte ich nennen diese Anwendung über eine .NET Process, und aktualisieren Sie eine WPF-Steuerelement (ProgressBar, speziell) asynchron:

private void Run() 
{ 
    Process process = new Process(); 
    process.StartInfo.FileName = "ConsoleApplication1.exe"; 
    process.StartInfo.UseShellExecute = false; 
    process.StartInfo.CreateNoWindow = true; 
    process.StartInfo.RedirectStandardOutput = true; 
    process.OutputDataReceived += new DataReceivedEventHandler((sender, e) => 
    { 
     if (e.Data != null) 
     { 
      Console.WriteLine(e.Data); 
      ProgressBar.Value = double.Parse(e.Data); 
     } 
    }); 
    process.Start(); 
    process.BeginOutputReadLine(); 
    process.WaitForExit(); 
    process.Close(); 
} 

Wenn ich dies ausführen schreibt es auf die Konsole ohne Problem, aber verständlicherweise beschwert sich über ProgressBar von einem anderen Thread zuzugreifen, so dass ich es mit wickeln Ein Aufruf:

Application.Current.Dispatcher.Invoke(() => 
{ 
    ProgressBar.Value = double.Parse(e.Data); 
}); 

Allerdings friert es ein, wenn es die Invoke trifft. Vermutlich kommt es zu einem Deadlock, obwohl ich nicht verstehe warum.

Umsehen, es scheint wie Process.SynchronizingObject wurde entwickelt, um diese Situationen zu beheben, aber ich kann nicht sehen, wie man es von WPF verwenden, da die Steuerelemente keine ISynchronizeInvoke Eigenschaft haben.

+0

Wenn Sie etwas wie public System.Windows.Threading deklarieren.Dispatcher dispatch = System.Windows.Threading.Dispatcher.CurrentDispatcher; 'Wenn die Anwendung initialisiert wird und diesen Dispatcher verwendet, ändert sich das Verhalten? – pay

+0

@pay: Keine Änderung, egal, ob ich 'dispatch' in' MainWindow' oder 'App' stecke. –

+0

Hm, also selbst wenn der Dispatcher auf dem UI-Thread erstellt wurde, ist es immer noch Deadlocks? Vielleicht auch aus dem Inneren des Event-Handlers entfernen. – pay

Antwort

-1

Ich würde empfehlen, Ihre ProgressBar an ein Objekt zu binden, das change notifications auslöst und die Eigenschaft für dieses Objekt aktualisiert. Auf diese Weise können Sie vermeiden, direkt die zu verwenden und auf die Ansicht zuzugreifen (was Sie wahrscheinlich sowieso nicht tun sollten).

private double _Progress = 0; 
public double Progress 
{ 
    get { return _Progress; } 
    set 
    { 
     if (value != _Progress) 
     { 
      _Progress = value; 
      OnPropertyChanged("Progress"); 
     } 
    } 
} 

// Dummy updates, 10% every second 
new Thread(p => 
{ 
    for (int i = 0; i < 10; i++) 
    { 
     Progress += 10; 
     Thread.Sleep(1000); 
    } 
}).Start(); 
<ProgressBar Value="{Binding Progress}" Height="20px" HorizontalAlignment="Stretch"/> 
+0

Können Sie Code zeigen, wie das funktionieren würde dieser Fall? –

+0

Das ist absolut trivial, ich bin sicher, dass Sie das tun können. –

+0

@CraigW: Ernsthaft? –

1

WaitForExit() macht den aktuellen Thread warten, bis der zugehörige Prozeß endet. Es sollte aufgerufen werden, nachdem alle anderen Methoden für den Prozess aufgerufen wurden. Verwenden Sie das Exit-Ereignis, um das Blockieren des aktuellen Threads zu vermeiden.

Quelle: MSDN

Also, sollten Sie nicht process.WaitForExit() verwenden aufgrund der Tatsache, dass es den UI-Thread machen zu warten, bis der Prozess zu beenden. Wenn Sie Code erst nach dem Beenden des Prozesses ausführen möchten, behandeln Sie das Ereignis process.Exited.

Auch process.ErrorDataReceived, process.OutputDataReceived und process.Exited sind in einem anderen Thread ausgelöst. Damit sie mit Kontrollen in dem UI-Thread zu interagieren (nämlich Ihre ProgressBar), sollten Sie die Ereignisse auf diese Weise handhaben:

private void process_OutputDataReceived(object sender, DataReceivedEventArgs e) 
    { 
     // do additional processing with e.Data if this is what you desire 
     ProgressBar.Dispatcher.Invoke(new Action(()=> { ProgressBar.Value=double.Parse(e.Data); })); 
    } 

ProgressBar.Dispatcher stellen sicher verwenden, dass der Dispatcher des Thread, in dem Ihr ProgressBar existiert wird genannt. Beachten Sie, dass ich diesen Code nicht ausgeführt habe, er ist von dem, was ich verwende, angepasst.

Bitte verzeihen Sie alle Fehler, zuerst posten. Es tut mir auch leid, dass ich einen so alten Post wiederbelebt habe, aber das kam von Google, und ich hoffe, ich kann jemandem helfen.