xaml - Binding fires on unloaded view/view-model when creating a new view -
if bind radiobutton view-model property using type converter, every time create view, setter on previous viewmodel gets called, though view unloaded , should not exist anymore. here minimum code reproduce issue:
1) define enum type:
enum enumtype { value1, value2, }
2) define convereter:
public class enumtypetobooleanconverter : ivalueconverter { public object convert(object value, type targettype, object parameter, string language) { return true; } public object convertback(object value, type targettype, object parameter, string language) { return enumtype.value1; } }
3) define view-model:
class viewmodel : inotifypropertychanged { private enumtype value; public viewmodel() { debug.writeline(string.format("viewmodel ({0})::ctor", this.gethashcode())); } public enumtype value { { debug.writeline(string.format("viewmodel ({0})::value::get", this.gethashcode())); return this.value; } set { debug.writeline(string.format("viewmodel ({0})::value::set", this.gethashcode())); if (this.value != value) { this.value = value; this.onpropertychanged(); } } } private void onpropertychanged([callermembername] string name = null) { if (this.propertychanged != null) { var ea = new propertychangedeventargs(name); this.propertychanged(this, ea); } } public event propertychangedeventhandler propertychanged; }
4) define usercontrol (view.xaml)
<usercontrol x:class="bindingissue.view" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:bindingissue" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:ignorable="d" d:designheight="300" d:designwidth="400" x:name="root"> <usercontrol.datacontext> <local:viewmodel x:name="viewmodel"/> </usercontrol.datacontext> <grid> <scrollviewer> <stackpanel> <radiobutton x:name="rdo1" content="value1" ischecked="{binding path=value, converter={staticresource enumtypetobooleanconverter}, converterparameter=value1, mode=twoway, updatesourcetrigger=propertychanged}"/> <button x:name="btnclose" click="btnclose_click" content="close"/> </stackpanel> </scrollviewer> </grid>
5) add code behind of view:
public view() { debug.writeline(string.format("view ({0})::ctor", this.gethashcode())); this.initializecomponent(); this.loaded += onloaded; this.unloaded += onunloaded; } private void btnclose_click(object sender, routedeventargs e) { if (this.parent popup) { debug.writeline("closing popup..."); ((popup)this.parent).isopen = false; } } private void onloaded(object sender, routedeventargs e) { debug.writeline(string.format("view ({0})::loaded", this.gethashcode())); } private void onunloaded(object sender, routedeventargs e) { debug.writeline(string.format("view ({0})::unloaded", this.gethashcode())); }
6) mainpage (xaml)
<grid background="{themeresource applicationpagebackgroundthemebrush}" x:name="grid"> <button x:name="btnnewview" click="btnnewview_click" content="new view" margin="4"/> </grid>
7) add event handler mainpage
private void btnnewview_click(object sender, routedeventargs e) { debug.writeline("opening new popup..."); view view = new view(); view.horizontalalignment = horizontalalignment.center; view.verticalalignment = verticalalignment.center; popup popup = new popup(); popup.child = view; popup.horizontaloffset = 300; popup.verticaloffset = 300; popup.isopen = true; }
opening , closing popups multiple times results following output (please keep track of hash codes):
opening new popup...
view (46418718)::ctor
viewmodel (59312528)::ctor
viewmodel (59312528)::value::get
view (46418718)::loaded
closing popup...
view (46418718)::unloaded
opening new popup...
view (58892413)::ctor
viewmodel (61646925)::ctor
viewmodel (61646925)::value::get
viewmodel (59312528)::value::set
view (58892413)::loaded
closing popup...
view (58892413)::unloaded
which means setter viewmodel created in unloaded view model being called little bit strange. behavior same both x:bind , binding.
i know if there explanation on behavior.
to clarify more: brand new pair of view/view-model instances created each time when new view being loaded, setter on previous instance of view-model being called. previous instance of view unloaded , should not exist @ point. (think of popup being closed each time, , there not event reference old view/view-model.)
which means setter viewmodel created in unloaded view model being called little bit strange
firstly, setter
not called when view unloaded, called when loading view. can add loading event handle verify this. adding loading event code code behind of view
control follows:
this.loading += view_loading; private void view_loading(frameworkelement sender, object args) { debug.writeline(string.format("view ({0})::loading", this.gethashcode())); }
and output be:
closing popup... view (22452836)::unloaded opening new popup... view (58892413)::ctor viewmodel (61646925)::ctor view (58892413)::loading viewmodel (61646925)::value::get viewmodel (19246503)::value::set view (58892413)::loaded
secondly, need why setter
called in scenario. 1 because set binding mode twoway
. if remove property follows not see setter
called since viewmodel
doesn't need know changes in view
.
<radiobutton x:name="rdo1" content="value1" ischecked="{binding path=value, converter={staticresource enumtypetobooleanconverter}, converterparameter=value1, updatesourcetrigger=propertychanged}"/>
more details binding mode please reference this article. reason may specific radiobutton
control. radiobutton
can cleared clicking radiobutton in same group, cannot cleared clicking again. when set ischecked
property true, thought property value of group updated. trigger twoway
binding. in scenrio, can test setting default value of ischecked
false follows, , find setter
not called until select rdo1
on ui. or can use control checkbox
testing not call setter
until ischecked
value updated.
public class enumtypetobooleanconverter : ivalueconverter { public object convert(object value, type targettype, object parameter, string language) { return false; } public object convertback(object value, type targettype, object parameter, string language) { return enumtype.value1; } }
the behavior not same if scrollviewer gets removed view behavior not same lets boolean property
for these 2 scenarios, tested on side. result same outputs above. since don't know how bind boolean
property, mentioned, whether setter
called depend on binding mode , whether set or update property. testing code binding boolean
follows have same outputs.
view.xaml
<radiobutton x:name="rdo2" content="boolvalue" ischecked="{binding path=boolvalue, converter={staticresource enumtypetobooleanconverter}, converterparameter=value1, mode=twoway, updatesourcetrigger=propertychanged}"/>
converter:
public class enumtypetobooleanconverter : ivalueconverter { public object convert(object value, type targettype, object parameter, string language) { return true; } public object convertback(object value, type targettype, object parameter, string language) { //return enumtype.value1; return true; } }
viewmodel;
private bool boolvalue; public bool boolvalue { { debug.writeline(string.format("viewmodel ({0})::boolvalue::get", this.gethashcode())); return this.boolvalue; } set { debug.writeline(string.format("viewmodel ({0})::boolvalue::set", this.gethashcode())); if (this.boolvalue != value) { this.boolvalue = value; this.onpropertychanged(); } } }
Comments
Post a Comment