Background mit Dispatcher scheint nicht, etwas zu tun
-
25-09-2019 - |
Frage
Ich versuche, eine ObservableCollection
zu aktualisieren, die an die UI gebunden ist, Daten. Ich weiß, dass dies Ich muß Gebrauch machen Dispatcher
und BeginvInvoke()
, und es so zu machen, dass die UI-up nicht einfrieren, wenn ich dies tun, indem ein Background ein guter Weg, um darüber zu gehen. Auf jeden Fall habe ich das alles, kompiliert und ausgeführt wird, aber nichts passiert. Ich brauche die Benutzeroberfläche alle 2 Minuten oder so zu aktualisieren, so dass ich auch eine DispatcherTimer
Das funktioniert, weil DispatcherTimer Teil Dispatcher, sondern friert die Benutzeroberfläche:
DispatcherTimer dispTimer = new DispatcherTimer();
dispTimer.Tick += dispTimer_Tick;
dispTimer.Interval = new TimeSpan(0, 0, 45);
dispTimer.Start();
private void dispTimer_Tick(object sender, EventArgs e)
{
PartialEmployees.Clear();
}
Also, mit der Background ich zusammengestückelt folgt aus:
DispatcherTimer dispTimer = new DispatcherTimer();
dispTimer.Tick += dispTimer_Tick;
dispTimer.Interval = new TimeSpan(0, 0, 45);
dispTimer.Start();
private void dispTimer_Tick(object sender, EventArgs e)
{
BackgroundWorker _worker = new BackgroundWorker();
_worker.DoWork += DoWork;
_worker.RunWorkerAsync();
}
private void DoWork(object sender, DoWorkEventArgs e)
{
Dispatcher.CurrentDispatcher.BeginInvoke( new Action(()=>
{
PartialEmployees.Clear();
}));
}
Aber nichts passiert mit der Benutzeroberfläche. Was mir fehlt / nicht richtig zu tun?
Lösung
Sie haben zwei Probleme:
-
Wenn Sie
Dispatcher.CurrentDispatcher
aus dem Hintergrund-Thread verwenden, wird der Dispatcher-Hintergrund-Thread bekommen, nicht die Dispatcher-UI-Thread. -
Aus Ihrer Beschreibung entnehme ich, dass Ihre
PartialEmployees.Clear()
Methode erhebliche Zeit in Anspruch nimmt auszuführen, und Sie mögen während der Ausführung des UI-Thread vermeiden sperren. Allerdings wird eine Background invokePartialEmployees.Clear()
auf dem UI-Thread, die die gleiche Wirkung wie die DispatcherTimer verwenden, so dass Sie eine andere Lösung als die benötigen für Sie gehen.
Wenn Sie nur das Dispatcher.CurrentDispatcher Problem lösen wollen, speichern Sie nur die aktuelle Dispatcher in einer lokalen Variablen wie folgt aus:
private void dispTimer_Tick(object sender, EventArgs e)
{
var uiDispatcher = Dispatcher.CurrentDispatcher;
BackgroundWorker _worker = new BackgroundWorker();
_worker.DoWork += (sender, e) =>
uiDispatcher.BeginInvoke(new Action(() =>
{
PartialEmployees.Clear();
}));
_worker.RunWorkerAsync();
}
Dies wird dazu führen, dass UI Änderung zu arbeiten, aber es wird immer noch die Benutzeroberfläche während der Änderung einsperren, genau so, wie wenn Sie nicht Background benutzt hatten. Der Grund dafür ist:
- Das DispatcherTimer Feuer, auf dem UI-Thread ausgeführt wird. Alles, was es ist (dispTimer_Tick) ist ein Background und beenden Sie dann starten.
- Die Background führt auf eigenem therad. es tut, ist alles Zeitplan ein Rückruf Dispatcher und dann beenden.
- Die Dispatcher Rückruf läuft auf dem UI-Thread wieder. Er fordert PartialEmployees.Clear (), die eine Weile dauert, Ihre UI-Thread Perren, während sie ausgeführt wird.
So Ihr Verhalten ist das gleiche, als ob der DispatcherTimer Rückruf PartialEmployees.Clear genannt hatte () direkt. In jedem Fall ist die zeitaufwendige Operation auf dem UI-Thread ausgeführt wird,
Der Grund für die Überbrückungs ist, dass jedes Mal, wenn Sie auf der Benutzeroberfläche ein großes Stück Arbeit zu tun Faden erhalten Sie eine momentane Überbrückungs bekommen, während es läuft. Die Lösung ist Ihre Arbeit in kleinere Teile zu brechen und ihnen einen nach dem anderen, entweder von einem DispatcherTimer oder einem Background zu tun. In Ihrem Fall prüfen Sie den Code für PartialEmployees.Clear()
zu sehen, ob es schrittweise durchgeführt werden kann.
Andere Tipps
Das Problem hierbei ist, dass Sie die Methode Dispatcher.CurrentDispatcher
von der Rückseite Grundfaden verwenden. Was Sie brauchen, ist die Dispatcher
-Instanz für den UI-Thread.
_worker.DoWork += delegate { DoWork(Dispatcher.CurrentDispatcher); };
...
private void DoWork(Dispatcher dispatcher) {
dispatcher.BeginInvoke(new Action(() => {
PartialEmployees.Clear();
});
}
Ich denke nicht, daß Sie die Hintergrundarbeit benötigen, wie BeginInvoke
auf dem Dispatcher
läuft auf einem Threadpool-Thread.
so etwas wie dies funktionieren soll, und ist prägnanter
DispatcherTimer dispTimer = new DispatcherTimer
{Interval = TimeSpan.FromSeconds(45)};
dispTimer.Tick += (o,e) => Dispatcher.CurrentDispatcher
.BeginInvoke((Action)PartialEmployees.Clear);
dispTimer.Start();