2009-04-02 12 views
1

Ich baue eine UI für ein Programm, und ich kann nicht herausfinden, warum meine Fortschrittsbalken nicht sichtbar werden, nachdem die Schaltfläche zum Konvertieren geklickt hat.C# Winforms Wie update ToolStrip in Funktion

private void convertButton_Click(object sender, EventArgs e) 
{ 
    toolStripProgressBar.Visible = true; 

    ... 

    toolStripProgressBar.Visible = false; 
} 

Ich lief in ein ähnliches Problem mit tkinter in Python, und ich hatte eine Funktion aufzurufen, die Untätigkeits Aufgaben zu aktualisieren. Gibt es eine Möglichkeit, dies mit Windows-Formularen ohne Verwendung von Threads zu tun?

Bearbeiten: Nebenbei bemerkt, dies ist ein Fortschrittsbalken in einem ToolStrip, der auch eine Beschriftung enthält, die mit Statusleiste Text aktualisiert wird. Gibt es eine Möglichkeit, das Label auf der linken Seite und den Fortschrittsbalken auf der anderen statt links neben einander zu bekommen?

Antwort

4

Nun, es ist eine Möglichkeit, dies zu tun, ohne Verwendung von Threads (Application.DoEvents), aber ich stark empfehlen gegen Sie es verwenden. Re-Entrance ist unangenehm und Sie möchten den UI-Thread überhaupt nicht binden.

Verwenden Sie stattdessen BackgroundWorker - es ist einfach, und es ist ziemlich entwickelt für Fortschrittsbalken. Sie können problemlos einen separaten Thread verwenden und den Fortschritt an den UI-Thread zurückmelden. Keine Notwendigkeit für Control.Invoke etc - es kümmert sich darum für Sie.

Es gibt lots of tutorials for BackgroundWorker - es sollte nicht zu lange dauern, um damit zu beginnen.

+0

Ich nehme an, Sie empfehlen, den langen laufenden Prozess zu einem separaten Thread zu bewegen, die Benutzeroberfläche nutzbar jederzeit korrekt zu halten? –

+0

Ja - das macht BackgroundWorker einfach. –

+0

Schätzen Sie die Hilfe. Ich hatte gehofft, Threads zu vermeiden, da sie manuell ziemlich schwierig sind und das ist eine einfache Benutzeroberfläche, aber das klingt perfekt. Vielen Dank. –

1

Mit der Frage, die Sie für die Möglichkeit, dies OHNE Threads zu tun, das ist es mit Application.DoEvents() zu tun. (Fügen Sie diesen Anruf direkt hinzu, nachdem Sie den Fortschrittsbalken als sichtbar gesetzt haben.)

Jetzt stimme ich Jon Skeet zwar zu, dass BackgroundWorker eine bessere Methode ist, dies zu tun, aber es verwendet einen separaten Thread.

1

Sie müssen Ihren Prozess in einem Thread ausführen, der vom UI-Thread getrennt ist, und ihn dann regelmäßig an den UI-Thread zurückmelden, wenn dieser läuft. Wenn Ihre Konvertierungsoperation innerhalb des Benutzeroberflächenthreads ausgeführt wird, reagiert sie nicht mehr, bis der Vorgang abgeschlossen ist.

1

Ich schätze das Problem ist, dass die "..." in Ihrem Code ein lang andauernder Prozess ist. UI-Aktualisierungen erfolgen nicht sofort, sondern müssen in Fenstern in der Nachrichtenwarteschlange ausgeführt und dann auf dem Bildschirm angezeigt werden. Die Warteschlange wird gepumpt und das Malen findet im selben Thread wie Ihre Events statt.

Als Ergebnis müssen alle länger laufenden Aufgaben in einen anderen Thread verschoben werden. Darüber hinaus muss Ihre Zeile Code aufgerufen werden, nachdem der Thread beendet wird. Andernfalls stellen Sie den Fortschrittsbalken ein und schalten ihn sofort wieder aus.

Ein Weg, dies zu tun, ist mit einem BackgroundWorker-Steuerelement.

1

Der Fortschrittsbalken kann nur sichtbar werden, wenn es erlaubt ist zu malen, was während der Verarbeitung von Nachrichten auftritt. Die Nachrichtenverarbeitung kann normalerweise nicht stattfinden, während Sie sich in der Mitte eines Ereignishandlers befinden. Wenn Sie möchten, dass der Fortschrittsbalken angezeigt wird, müssen Sie visibility auf true setzen, einen Hintergrund-Thread starten, um die Arbeit abzuschließen und vom Handler zurückzukehren.

1

geht hier zwei Links versuchen, Ihnen zu erklären, wie die Dinge funktionieren: (1)(2)

Nun, ich werde versuchen, es zu erklären, so kurz wie ich kann. Das meiste, was in einer Windows Forms-Anwendung passiert, geschieht in einem einzigen Thread, normalerweise im selben Thread, in dem Main() ausgeführt wird. Wenn Sie Program.cs öffnen, sehen Sie, dass Main() eine Zeile hat, die wie folgt aussieht:

Wenn Sie die Anwendung zu jedem beliebigen Zeitpunkt debuggen und den Aufruf-Stapel untersuchen, werden Sie feststellen, dass es auf diese Run-Methode zurückverfolgt wird. Dies bedeutet, dass eine Windows Forms-Anwendung tatsächlich eine fortlaufende Ausführung der Run-Methode ist. Also, was macht Run? Run isst eine Nachrichtenwarteschlange, über die Windows Nachrichten an sie sendet. Run sendet dann diese Nachrichten an die richtigen Steuerelemente, die selbst Dinge wie das Hinzufügen von Text zum gedrückten Schlüssel, Neuzeichnen usw. vornehmen. Beachten Sie, dass dies alles während und während der Endlosschleife neben einem einzelnen Thread ausgeführt wird, also wenn Sie gerade tippen oder einfach das Fenster herum bewegen, viele dieser Nachrichten werden an die Anwendung weitergegeben, die wiederum sie verarbeitet und entsprechend reagiert, alles in diesem einzelnen Thread. Controls können über die Warteschlange auch Nachrichten an sich selbst senden und sogar über Control.BeginInvoke können Sie Nachrichten in der Pumpe platzieren. Eine der Sachen, die diese Kontrollen tun, ist Ereignisse entsprechend dem, was passiert, auszulösen. Wenn Sie also auf eine Schaltfläche klicken, wird der Code, den Sie für diesen Klick geschrieben haben, letztendlich und indirekt von der Application.Run-Methode ausgeführt.

Nun, was passiert mit Ihrem Code ist, dass, obwohl Sie den sichtbaren Status Ihrer Fortschrittsbalken zu sichtbar ändern und dann aktualisieren Sie den Wert, Sie ändern dann seine Sichtbarkeit in false, alle in der gleichen Methode. Das bedeutet, dass Application.Run() erst nach dem Verlassen der Methode fortfahren kann, die Nachrichtenwarteschlange zu durchlaufen und zu konsumieren, wodurch der Fortschrittsbalken aufgefordert wird, seine Anzeige zu aktualisieren. Wenn dies geschieht, haben Sie die Sichtbarkeit der Fortschrittsleiste bereits auf "Falsch" gesetzt, das letzte, was Sie vor dem Beenden der Methode getan haben. DoEvents() ist eine schnelle und schmutzige Lösung für Ihr Problem, wie es die Nachrichten in der Warteschlange liest und verarbeitet. Ich fühle mich nicht wohl dabei, es zu benutzen, da es Probleme mit der Wiedereintrittsfähigkeit bringt.

Verwenden von Threads ist eine gute Lösung, aber ich würde empfehlen, einen ThreadPool-Thread anstelle eines benutzerdefinierten Threads in einer solchen Situation zu verwenden, da ich benutzerdefinierte Threads nur in Fällen verwende, in denen ich eine begrenzte Anzahl von langlebigen Threads habe und ich muss ihre Lebenszyklen kontrollieren. Die einfachste und praktischste Methode zum Verwenden von Threads ist die Verwendung der BackgroundWorker-Komponente, obwohl ich Ihnen empfehlen würde, sich mit dem Delegieren von Windows Forms-Multithreading vertraut zu machen, wenn Sie wirklich verstehen wollen, was vor sich geht.

+0

Danke für die Panne. Die BackgroundWorker-Lösung hat funktioniert, und ich werde in Zukunft auf Delegierte eingehen. –

0

Meine Lösung ist, Refresh auf dem Statusstreifen aufzurufen.
Ich glaube, dass der UI-Thread den Statusstreifen neu streichen wird.

toolStripStatusBar1.PerformStep(); 
statusStrip1.Refresh(); 

Dies ist für .NET 4.0. Obwohl diese Frage alt ist, war es die erste, die ich beim googlen dieses Problems fand.