c# - Observe property change multiple times in one WPF event using MVVM? -


i wanting costly operation , post user 'where' in state of operation method was. using mvvm bind icommand button click event. event triggers dialogue user, file select word document parsed, form filled word document. problem run standard operation text displays last change property. have set breakpoints , see property gets raised, seems icommand argument waits till work finished , updates last property. there way around show posts backs user, while process happening?

**so want user click button , see "obtained word document", (work done) "parsed word document" 1 after other process completes. not last change when icommand finishes. think core issue ui not getting changes till stack pauses inside either 'relay command'/'async relay command' delegate method. **

xaml:

<textbox text="{binding wordfilelocation}" /> <button content="start process" height="20" command="{binding asyncdocommand}"/> <textbox text="{binding text, isasync=true}" /> 

viewmodel:

    private reader _wordreader = new reader();     private string _parsedwordstring;     private asyncrelaycommand _doasynccommand;     private string _text;     private string _wordfilelocation;      public string text     {         { return _text; }         set         {             _text = value;             raisepropertychanged("text");         }     }      public string wordfilelocation     {         { return _wordfilelocation; }         set         {             _wordfilelocation = value;             raisepropertychanged("wordfilelocation");         }     }      public icommand asyncdocommand     {                 {             if (_doasynccommand == null)             {                 _doasynccommand = new asyncrelaycommand(async () => await doit());              }              return _doasynccommand;         }     }      public async task doit()     {                     wordfilelocation = "somewhere dialogue selected...";         text = "looking....";          await task.delay(2000);         text = "look @ me";  // works finally....          await getworddata();           // if put in delay below, text change show up.  if not won't.  reason setting of text not show till delay triggered.         //await task.delay(100);          await parseworddata();     }      async task parseworddata()     {         try         {             _parsedwordstring = _wordreader.readworddocwithforms(_wordfilelocation);             text = "parsed word document";         }         catch (exception)         {             text = "could not parse word document";         }     }      async task getworddata()     {         openfiledialog dlg = new openfiledialog();         dlg.multiselect = false;         dlg.filter = "doc files (*.doc, *.docx)|*.doc;*.docx";          // open dialog         bool ok = (bool)dlg.showdialog();          if(ok)         {             try             {                 // location dialog                 wordfilelocation = dlg.filename;                 text = "obtained word document.";             }             catch (exception)             {                 text = "failed loading document.";             }         }         else         {             text = "could not browse document.";         }     } 

edit 8-20-14 12:45 pst: tseng correct except 1 thing. cannot ui accept async changes unless force 'task.delay(100)'. stack wants auto finish through 2 sub methods. total noob @ .net 4.5 async methods, want use them seem preferred way. guessing ignorance in understanding 'task' , does. have task return seems await not simple 'await "loaded"' or similar. have tried return types in signature method 'void', task, task simple 'return "obtained document"'. none of updates property, untill call task.delay() after sub method. ignorance of understanding async process of why need pause update. 'parseworddocument' pretty expensive parsing long word documents , on average takes 2 5 seconds depending on doc size parsing out form fills plain text. delay text not getting updated till sub method done.

i'd suggest use async command implementation, asyncrelaycommand found on internet.

i use implementation 1 of own mvvm projects.

public class asyncrelaycommand : icommand {     protected readonly func<task> _asyncexecute;     protected readonly func<bool> _canexecute;      public event eventhandler canexecutechanged {         add { commandmanager.requerysuggested += value; }         remove { commandmanager.requerysuggested -= value; }     }      public asyncrelaycommand(func<task> execute)         : this(execute, null) {     }      public asyncrelaycommand(func<task> asyncexecute, func<bool> canexecute) {         _asyncexecute = asyncexecute;         _canexecute = canexecute;     }      public bool canexecute(object parameter) {         if(_canexecute == null) {             return true;         }          return _canexecute();     }      public async void execute(object parameter) {         await executeasync(parameter);         // notify ui commands can execute changed may have changed         raisecanexecutechanged();     }      protected virtual async task executeasync(object parameter) {         await _asyncexecute();     }     public void raisecanexecutechanged()      {         commandmanager.invalidaterequerysuggested();     } } 

this has additional benefit, can't run command async , ui operations inbetween (i.e. add observablecollection) can notify ui when canexecute status may changed (i.e. when command finished).

example usage:

public icommand docommand {         {         if(_docommand == null)         {             _docommand = new asyncrelaycommand(doit);         }          return _docommand;     } }  public async void doit() {     wordfilelocation = "someplace dialogue selected";     await parsedocument();      text = "parsed word document";      await obtaindocument();     text = "obtained word document.";   } 

edit: wpf command bindings async/task aware. if icommand.execute returns task or task<t>, wpf run them asynchronously.

you need make sure both, criteria met:

  1. your doit() method has async keyword (c# 5.0/.net 4.5) (or returns task rather being void, .net 3.5 , 4.0)
  2. you use await every long processing. if method returns awaitable/task/task<t> can await on it. if methods doesn't, can still create new task , await it

another example of doit() method

public task parsedocumentasync()  {     return task.run( () => {         // long processing parsing code here     }); }  public async void doit() {     wordfilelocation = "someplace dialogue selected";      text = "begin";     await parsedocumentasync(); // public task parsedocumentasync() { }      text = "parsedocumentdone()";      text = "wait 3 seconds";     await task.delay(3000);      text = "run non-task methods";     task.run( () => longrunningnonasyncmethod(); );      text = "longrunningnonasyncmethod() finished. wait 2 seconds";      // don't this. block ui thread!      // has no await, runs on thread started everything,      // ui thread in case, because view invoked command.     // that's why locks ui     thread.sleep(2000);       text = "waited 2 seconds. won't see this, because ui locked";     // don't this, block ui thread.      longrunningnonasyncmethod();       text = "finished";   } 

on side note: if using .net 4.5 , c# 5.0, can use async/await keywords async operations. if forced use older frameworks (.net 3.5 , 4.0), can still use task t = task.run(...) start , `t.continuewith( () => { text = "finished" } )´ execute code after task finished.

edit2: sorry late reply, busy rl work, didn't had time watch in here. i'll update parseworddata() method , hope works then.

// alternatively: async void parseworddata().  // async void => task return type // async task => task<task> return type task parseworddata()  {     return task.run( () => {         try         {             _parsedwordstring = _wordreader.readworddocwithforms(_wordfilelocation);             text = "parsed word document";         }         catch (exception)         {             text = "could not parse word document";         }     }); } 

this run readworddocwithforms code inside thread/task , return task. task can awaited.

basically boils down to: use await on awaitable methods (which return task or task<t>) , if need run method isn't awaitable, use task.run(...) , return (or await) task.


Comments

Popular posts from this blog

java - How to specify maven bin in eclipse maven plugin? -

single sign on - Logging into Plone site with credentials passed through HTTP -

php - Why does AJAX not process login form? -