WPF-modalen Fenster,
Ich entschuldige mich wenn diese Frage schon beantwortet, Tonnen von Zeiten, aber ich kann nicht scheinen, um eine Antwort zu finden, das funktioniert für mich. Ich möchte zum erstellen eines modalen Fensters zeigt verschiedene Statusmeldungen, während meine Anwendung führt einen lang andauernden Aufgaben. Diese Aufgaben werden in einem separaten thread ausgeführt, und ich bin in der Lage zu aktualisieren, den text auf das Fenster Fortschritt in den verschiedenen Phasen des Prozesses. Die cross-thread-Kommunikation ist alles schön funktioniert. Das problem ist, dass ich kann nicht das Fenster auf der Oberseite nur andere windows-Anwendung (nicht jede Anwendung auf dem computer), Aufenthalt auf die Oberseite, dass keine Interaktion mit dem übergeordneten Fenster, und immer noch erlauben, die Arbeit fortzusetzen.
Hier ist, was ich bisher ausprobiert habe:
Erste, meine splash-Fenster ist eine benutzerdefinierte Klasse, die Sie erweitert die Fensterklasse und verfügt über Methoden zum update der Meldung. Ich erstelle eine neue Instanz der splash-Klasse vorzeitig auf und Zeigen/Verstecken, wie gebraucht.
In den einfachsten Fällen, instanziiere ich die Fenster und rufen .Show()
drauf:
//from inside my secondary thread
this._splash.Dispatcher.Invoke(new Action(() => this._splash.Show());
//Do things
//update splash text
//Do more things
//close the splash when done
this._splash.Dispatcher.Invoke(new Action(() => this._splash.Hide());
Diese korrekt zeigt der Fenster-und weiterhin läuft mein code für die Initialisierung Aufgaben, aber es erlaubt mir, klicken Sie auf das übergeordnete Fenster und bringen diese nach vorne.
Als Nächstes habe ich versucht, das deaktivieren das Hauptfenster und die re-Aktivierung später:
Application.Current.Dispatcher.Invoke(new Action(() => this.MainWindow.IsEnabled = false));
//show splash, do things, etc
Application.Current.Dispatcher.Invoke(new Action(() => this.MainWindow.IsEnabled = true));
Dies deaktiviert alle Elemente in das Fenster, aber ich kann immer noch auf die Hauptfenster und bringen es vor dem splash-screen, das ist nicht das, was ich will.
Als Nächstes habe ich versucht, mit dem topmost-Eigenschaft auf dem splash-Fenster. Diese hält er vor allem, und in Verbindung mit Einstellung Hauptfenster IsEnabled-Eigenschaft konnte ich verhindern, dass Interaktion, aber das macht den splash-screen erscheinen vor den ALLES, auch andere Anwendungen. Ich will nicht, dass entweder. Ich will einfach nur, dass es das oberste Fenster in der Anwendung.
Dann fand ich Beiträge über die Verwendung .ShowDialog()
statt .Show()
. Ich habe versucht, und Sie ordnungsgemäß zeigte, den dialog und die hat mir nicht erlauben, klicken Sie auf das übergeordnete Fenster, aber das aufrufen .ShowDialog()
lässt das Programm warten auf Sie, um das Dialogfenster zu schließen, bevor es weiter ausgeführt wird, code. Dies ist offensichtlich nicht das, was ich möchte. Ich nehme an, ich könnte ShowDialog()
auf einen anderen thread, damit dieser thread hängen würde, aber der thread macht die Arbeit nicht wäre...) ist, dass die empfohlene Methode?
Habe ich das auch als Möglichkeit, nicht mit einem Fenster, und, statt putting ein full-size-Fenster-element in der front, die alles andere auf der Seite. Das würde funktionieren, außer, dass ich andere Fenster, die ich öffne, und ich möchte in der Lage sein zu verwenden, der splash-screen, wenn diejenigen, die offen sind auch. Wenn ich eine Fenster-element, würde ich die neu erstellen auf jedes Fenster, und ich wäre nicht in der Lage zu verwenden mein handy UpdateSplashText
Methode in meine custom splash Klasse.
Also das bringt mich zu der Frage. Was ist der richtige Weg, dies zu behandeln?
Vielen Dank für Ihre Zeit und sorry für die lange Frage, aber die details sind wichtig 🙂
Du musst angemeldet sein, um einen Kommentar abzugeben.
Sind Sie richtig, dass
ShowDialog
gibt Ihnen die meisten der UI-Verhalten, das Sie möchten.Er hat das problem, dass, sobald Sie es nennen blockieren Sie die Ausführung aber. Wie könnten Sie vielleicht ausführen von code, nachdem Sie zeigen die form, sondern definieren, was es sein sollte, bevor es angezeigt wird? Das ist Ihr problem.
Könnte man nur tun all die Arbeit, die innerhalb der splash-Klasse, aber das ist eher schlechte Praxis aufgrund der engen Kopplung.
Was Sie tun können nutzen Sie die
Loaded
FalleWindow
zu definieren, der code, der ausgeführt werden soll, nachdem das Fenster angezeigt, aber wo es definiert wird, bevor Sie es zeigen.Beachten Sie, dass diese Methode dient zum Kapseln der boilerplate-code hier, so dass Sie passieren können, in jedem Arbeiter-Methode akzeptiert die Statusanzeige und es wird, dass die Arbeit in einem hintergrund-thread, während zeigt ein generisches splash-Bildschirm, der Fortschritt angegeben für die Arbeitnehmer.
Diese könnte dann aufgerufen werden, so etwas wie dieses:
void Foo(Action<Action<int, string>> work)
kapselt alle die nicht Projekt-spezifische Sachen, so dass es wiederverwendet werden kann für verschiedene Aufgaben, wo Sieworker.DoWork += delegate { work(worker.ReportProgress); };
. So ist es sowohl entkoppelt und wiederverwendbar.IProgress<string>
- Objekt anstelle einesAction<int, string>
; würde es helfen, tragen die Semantik besser.async
und MVVM Ansatz.Können Sie die
Visibility
Eigenschaft aufWindow
zu verstecken das gesamte Fenster, während der splash-screen läuft.XAML
Code
Können Sie Ihre Fortschritte Fenster Konstruktor nehmen
Task
und dann sicherstellen, dass das Fenster Aufrufetask.Start
auf dieOnLoaded
Veranstaltung. Dann nutzen SieShowDialog
von der übergeordneten form, die bewirkt, dass das Fenster zum starten der Aufgabe.Beachten Sie auch nennen könnte
task.Start
im Konstruktor, oder in der übergeordneten form überall vor dem AufrufShowDialog
. Je nachdem, was für Sie am sinnvollsten ist.Andere Möglichkeit wäre einfach mit einem Fortschrittsbalken in der status-Streifen des Hauptfensters, und loszuwerden des popup. Diese option scheint mehr und mehr üblich in diesen Tagen.
Akzeptiert die Antwort von @Servy hat mir sehr geholfen! Und ich wollte zu teilen meine Version mit der
async
- und MVVM-Ansatz. Es enthält auch eine kleine Verzögerung zu vermeiden "Fenster " flackern" zu schnelle Operationen.Dialog-Methode:
Verwendung:
Nochmals vielen Dank @Servy, mich gerettet eine Menge Zeit.
Habe ich einen Weg gefunden, um diese Arbeit durch den Aufruf
ShowDialog()
auf einem separaten thread. Ich habe meine eigenenShowMe()
undHideMe()
Methoden in meine dialog-Klasse, die mit der Arbeit. Ich habe auch capture theClosing
Ereignis zu verhindern, schließen Sie den dialog, so kann ich wieder verwenden.Hier ist mein code für mein splash-screen-Klasse: