c# - How to let the UI refresh during a long running *UI* operation -


before flag question being duplicate, hear me out.

most people have long running non-ui operation doing , need unblock ui thread. have long running ui operation must run on ui thread blocking rest of application. basically, dynamically constructing dependencyobjects @ run time , adding them ui component on wpf application. number of dependencyobjects need created depends upon user input, of there no limit. 1 of test inputs have has 6000 dependencyobjects need created , loading them takes couple minutes.

the usual solution of using background worker in case not work, because once dependencyobjects created background worker, can no longer added ui component since created on background thread.

my current attempt @ solution run loop in background thread, dispatch ui thread each unit of work , calling thread.yield() give ui thread chance update. works - ui thread chance update couple times during operation, application still blocked.

how can application keep updating ui , processing events on other forms during long running operation?

edit: requested, example of current 'solution':

private void initializeform(list<nondependencyobject> mycollection) {     action<nondependencyobject> dowork = (nondepobj) =>         {             var dependencyobject = createdependencyobject(nondepobj);             uicomponent.add(dependencyobject);             // set binding on each dependencyobject , update progress bar             ...         };      action background = () =>         {             foreach (var nondependencyobject in mycollection)             {                  if (nondependencyobject.needstobeadded())                  {                      dispatcher.invoke(dowork, nondependencyobject);                      thread.yield();  //doesn't give ui enough time update                  }             }         };     background.begininvoke(background.endinvoke, null); } 

changing thread.yield() thread.sleep(1) seems work, solution?

sometimes indeed required background work on ui thread, particularly, when majority of work deal user input.

example: real-time syntax highlighting, as-you-type. might possible offload sub-work-items of such background operation pool thread, wouldn't eliminate fact text of editor control changing upon every new typed character.

help @ hand: await dispatcher.yield(dispatcherpriority.applicationidle). give user input events (mouse , keyboard) best priority on wpf dispatcher event loop. background work process may this:

async task douithreadworkasync(cancellationtoken token) {     var = 0;      while (true)     {         token.throwifcancellationrequested();          await dispatcher.yield(dispatcherpriority.applicationidle);          // ui-related work         this.textblock.text = "iteration " + i++;     } } 

this keep ui responsive , background work fast possible, idle priority.

we may want enhance throttle (wait @ least 100 ms between iterations) , better cancellation logic:

async task douithreadworkasync(cancellationtoken token) {     func<task> idleyield = async () =>         await dispatcher.yield(dispatcherpriority.applicationidle);      var cancellationtcs = new taskcompletionsource<bool>();     using (token.register(() =>         cancellationtcs.setcanceled(), usesynchronizationcontext: true))     {         var = 0;          while (true)         {             await task.delay(100, token);             await task.whenany(idleyield(), cancellationtcs.task);             token.throwifcancellationrequested();              // ui-related work             this.textblock.text = "iteration " + i++;         }      } } 

updated op has posted sample code.

based upon code posted, agree @highcore's comment proper viewmodel.

the way you're doing currently, background.begininvoke starts background operation on pool thread, synchronously calls ui thread on tight foreach loop, dispatcher.invoke. adds overhead. besides, you're not observing end of operation, because you're ignoring iasyncresult returned background.begininvoke. thus, initializeform returns, while background.begininvoke continues on background thread. essentially, fire-and-forget call.

if want stick ui thread, below how can done using approach described.

note _initializetask = background() still asynchronous operation, despite it's taking place on ui thread. you won't able make synchronous without nested dispatcher event loop inside initializeform (which bad idea because of implications ui re-entrancy).

that said, simplified version (no throttle or cancellation) may this:

task _initializetask;  private void initializeform(list<nondependencyobject> mycollection) {     action<nondependencyobject> dowork = (nondepobj) =>         {             var dependencyobject = createdependencyobject(nondepobj);             uicomponent.add(dependencyobject);             // set binding on each dependencyobject , update progress bar             ...         };      func<task> background = async () =>         {             foreach (var nondependencyobject in mycollection)             {                 if (nondependencyobject.needstobeadded())                 {                     dowork(nondependencyobject);                     await dispatcher.yield(dispatcherpriority.applicationidle);                 }             }         };      _initializetask = background(); } 

Comments

Popular posts from this blog

python - Subclassed QStyledItemDelegate ignores Stylesheet -

java - HttpClient 3.1 Connection pooling vs HttpClient 4.3.2 -

SQL: Divide the sum of values in one table with the count of rows in another -