c# - Using a Stopwatch and DataBinding on a WPF Window that is already being updated using IProgress -
in main() wpf program run time consuming method asynchronously. when method running, fire secondary window contains progressbar, update using iprogress.
following example of setup.
main program:
public partial class mainwindow : window { private progressbarwindow pbwwindow = null; public mainwindow() { initializecomponent(); } private void runmethodasync(iprogress<int> progress) { dispatcher.invoke(() => { pbwwindow = new progressbarwindow("processing..."); pbwwindow.owner = this; pbwwindow.show(); }); timeconsumingmethod(progress); } private void timeconsumingmethod(iprogress<int> progress) { (int = 1; <= 100; i++) { // thread.sleep() represents actual time consuming work being done. thread.sleep(100); progress.report(i); } } private async void btnrun_click(object sender, routedeventargs e) { iprogress<int> progress; progress = new progress<int>(i => pbwwindow.setprogressupdate(i)); await task.run(() => runmethodasync(progress)); } } my progressbarwindow contains progress bar looks this:
public partial class progressbarwindow : window { stopwatch stopwatch = new stopwatch(); backgroundworker worker = new backgroundworker(); public string elapsedtimestring { get; set; } public progressbarwindow(string infotext) { initializecomponent(); settimer(); } private void window_loaded(object sender, routedeventargs e) { starttimer(); } private void settimer() { worker.workerreportsprogress = true; worker.workersupportscancellation = true; worker.dowork += (s, e) => { while (!worker.cancellationpending) { worker.reportprogress(0, stopwatch.elapsed); thread.sleep(1000); } }; worker.progresschanged += (s, e) => { timespan elapsedtime = (timespan)e.userstate; elapsedtimestring = string.format("{0}:{1}:{2}", elapsedtime.minutes, elapsedtime.seconds, elapsedtime.milliseconds); }; } private void starttimer() { stopwatch.start(); worker.runworkerasync(); } private void stoptimer() { stopwatch.stop(); worker.cancelasync(); } public void setprogressupdate(int progress) { pbload.value = progress; if (progress >= 100) { stoptimer(); close(); } } } i borrowed stopwatch logic this answer. then, on progressbarwindow have textblock i've used binding follows, answer above says.
<textblock name="tbelapsedtime" text="{binding elapsedtimestring}"/> now when run program, method executes, , progress bar updates fine. however, textblock that's supposed update elapsed time not updated.
to verify timer's running fine, updated textblock value directly follows instead of binding , worked expected , displayed elapsed time:
worker.progresschanged += (s, e) => { timespan elapsedtime = (timespan)e.userstate; elapsedtimestring = string.format("{0}:{1}:{2}", elapsedtime.minutes, elapsedtime.seconds, elapsedtime.milliseconds); tbelapsedtime.text = elapsedtimestring; }; so i'm guessing problem binding , possibly using backgroundworker on windows that's being run asynchronously? how fix use databinding?
as mentioned ginger ninja, have implement inotifypropertychanged , use relativesource={relativesource self} (as additional setting binding):
public partial class mainwindow : window, inotifypropertychanged { public event propertychangedeventhandler propertychanged; private string _elapsedtimestring; public string elapsedtimestring { { return _elapsedtimestring; } set { if (_elapsedtimestring != value) { _elapsedtimestring = value; propertychanged?.invoke(this, new propertychangedeventargs("elapsedtimestring")); } } } // .... } and xaml:
<textblock name="tbelapsedtime" text="{binding elapsedtimestring, relativesource={relativesource self}}"/> data binding used in combination mvvm. imho prefered way solve problem... if want use mvvm, have implement view model contains logic , implements inotifypropertychanged. can bind properties view model view. ensures nice separation between (gui related) logic , view.
Comments
Post a Comment