c# - Prevent using Dispatcher.Invoke in WPF code -
i'm web , backend programmer nature. try avaoid making windows programs. have make wpf client.
i have background task raises event every time. (it working poller , when criteria met event raised). noob wrote code attached event update ui.
private void isdisconnectedevent() { userwindow.visibility = visibility.hidden; disconnectwindow.visibility = visibility.visible; }
this gives exception because not on same thread. after googling found should change code with:
private void isdisconnectedevent() { dispatcher.invoke(() => { userwindow.visibility = visibility.hidden; disconnectwindow.visibility = visibility.visible; }); }
this works, not event , makes code horrible ugly. there better ways this?
regarding this:
this works, not event , makes code horrible ugly
yes, wpf-based code extremely horrible unless understand , embrace the wpf mentality.
basically, interactions between custom logic (aka business logic or application logic) , wpf ui should manifest in form of declarative databinding opposed traditional imperative approach.
this means there should nothing this:
userwindow.visibility = visibility.hidden;
anywhere in code, because introducing things makes code dependent on ui , executable on ui thread.
instead, wpf approach declaratively databind visibility
propety of ui element (in xaml) relevant bool property can operate outside, this:
<userwindow visibility="{binding showuserwindow, converter={my:booltovisibilityconverter}}"> <!-- ... --> </userwindow>
then, need create relevant class contains properties ui expecting bind to. called viewmodel.
notice in order support two-way wpf databinding, viewmodels must implement inotifypropertychanged
interface.
when doing so, convenient have propertychanged
event interface marshalled ui thread, no longer have worry setting viewmodel's properties using dispatcher
.
therefore our first step have our viewmodels inherit class this:
(taken this answer):
public class propertychangedbase:inotifypropertychanged { public event propertychangedeventhandler propertychanged; protected virtual void onpropertychanged(string propertyname) { //raise propertychanged event on ui thread, relevant propertyname parameter: application.current.dispatcher.begininvoke((action) (() => { propertychangedeventhandler handler = propertychanged; if (handler != null) handler(this, new propertychangedeventargs(propertyname)); })); } }
once have our property change notification dispatch ui thread in place, can proceed create relevant viewmodel suits, in case, userwindow
, it's databinding expectations:
public class userviewmodel: propertychangedbase { private bool _showuserwindow; public bool showuserwindow { {return _showuserwindow; } set { _showuserwindow = value; onpropertychanged("showuserwindow"); //this important!!! } } }
finally, need set window's datacontext instance of it's corresponding viewmodel. 1 simple way in window's constructor:
public userwindow() //window's constructor { initializecomponent(); //this required. datacontext = new userviewmodel(); //here set datacontext }
as can see in example, there literally no need manipulate ui element's properties in procedural code. not because resolves thread affinity issues (because can set showuserwindow
property thread), because makes viewmodels , logic decoupled ui , testable , more scalable.
this same concept applies in wpf.
one detail need mention i'm making use of technique of combining markupextension
, ivalueconverter
in order reduce the xaml boilerplate involved in using converters.
you can read more in link , msdn databinding page linked above.
let me know if need further details.
Comments
Post a Comment