c# - WPF INotifyPropertyChanged without burning base class -
i'm trying find simple approach data binding in wpf.
i'm using inotifypropertychanged
interface , works fine if it's implemented on abstract base class , inherited objects have bound members.
public partial class mainwindow : window { public static mainwindow instance; private readonly vm _vm; public mainwindow () { initializecomponent(); datacontext = _vm = new vm { button1 = new vm.observablebutton(button1, new list<string> { "paused", "logging" }, false), button2 = new vm.observabletogglebutton(button2, new list<string> { "log all", "log vba" }, false), }; } private class vm { public abstract class observableobject : inotifypropertychanged { public event propertychangedeventhandler propertychanged; protected virtual void onpropertychanged ([callermembername] string propname = "") { var pc = propertychanged; if (pc != null) pc(this, new propertychangedeventargs(propname)); } } public class observablebutton : observableobject { private readonly button _b; private readonly list<string> _options; private string _content; public string content { { return _content; } set { if (_content == value) return; _content = value; onpropertychanged(); } } public boolean on { set; private get; } public observablebutton (button b, list<string> options, boolean on = true) { _b = b; _options = options; _b.click += click; on = on; content = on ? _options[0] : _options[1]; } public void click (object sender, routedeventargs e) { on = !on; content = on ? _options[0] : _options[1]; } } public class observabletogglebutton : observableobject { private readonly togglebutton _b; private readonly list<string> _options; private string _content; public string content { { return _content; } private set { if (_content == value) return; _content = value; onpropertychanged(); } } private boolean _on; public boolean on { private { return _on; } set { if (_on == value) return; _on = value; content = value ? _options[0] : _options[1]; } } public observabletogglebutton (togglebutton b, list<string> options, boolean on = true) { _b = b; _options = options; on = on; content = _b.ischecked ?? false ? _options[0] : _options[1]; } public void push () { var peer = new togglebuttonautomationpeer(_b); var toggleprovider = peer.getpattern(patterninterface.toggle) itoggleprovider; if (toggleprovider != null) toggleprovider.toggle(); //on = !on; } } public observablebutton button1 { get; set; } public observabletogglebutton button2 { get; set; } public vm () { } } } <grid margin="0,0,183,134"> <button x:name="button1" content="{binding button1.content}" horizontalalignment="left" margin="112,134,0,0" verticalalignment="top" width="75"/> <togglebutton x:name="button2" ischecked="{binding button2.on, mode=onewaytosource}" content="{binding button2.content}" horizontalalignment="left" margin="206,134,0,0" verticalalignment="top"/> </grid>
i wanted try doing without burning base class though, implemented inotifypropertychanged
on view model , routed change events bound members, through single interface on view model. though binding object has reference source , correct property name, fails silently.
i figured doesn't work because binding object type checking, made fake implementation on bound properties , works. here code scenario...
public partial class mainwindow : window { public static mainwindow instance; public mainwindow () { initializecomponent(); datacontext = new viewmodel { button1 = new viewmodel.observablebutton(button1, new list<string> { "paused", "logging" }, false), button2 = new viewmodel.observabletogglebutton(button2, new list<string> { "log all", "log vba" }, false), }; } public class viewmodel : inotifypropertychanged { private static viewmodel _instance; public event propertychangedeventhandler propertychanged; protected virtual void onpropertychanged<t> (t control, [callermembername] string propname = "") { var pc = propertychanged; if (pc != null) pc(control, new propertychangedeventargs(propname)); } public class observablebutton : inotifypropertychanged { public event propertychangedeventhandler propertychanged; protected virtual void onpropertychanged () {} private readonly button _b; private readonly list<string> _options; private string _content; public string content { { return _content; } set { if (_content == value) return; _content = value; _instance.onpropertychanged(this); } } public boolean on { set; private get; } public observablebutton (button b, list<string> options, boolean on = true) { _b = b; _options = options; _b.click += click; on = on; content = on ? _options[0] : _options[1]; } public void click (object sender, routedeventargs e) { on = !on; content = on ? _options[0] : _options[1]; } } public class observabletogglebutton : inotifypropertychanged { public event propertychangedeventhandler propertychanged; protected virtual void onpropertychanged () {} private readonly togglebutton _b; private readonly list<string> _options; private string _content; public string content { { return _content; } private set { if (_content == value) return; _content = value; _instance.onpropertychanged(this); } } private boolean _on; public boolean on { private { return _on; } set { if (_on == value) return; _on = value; content = value ? _options[0] : _options[1]; } } public observabletogglebutton (togglebutton b, list<string> options, boolean on = true) { _b = b; _options = options; on = on; content = _b.ischecked ?? false ? _options[0] : _options[1]; } } public observablebutton button1 { get; set; } public observabletogglebutton button2 { get; set; } public viewmodel () { _instance = this; } } } <grid margin="0,0,183,134"> <button x:name="button1" content="{binding button1.content}" horizontalalignment="left" margin="112,134,0,0" verticalalignment="top" width="75"/> <togglebutton x:name="button2" ischecked="{binding button2.on, mode=onewaytosource}" content="{binding button2.content}" horizontalalignment="left" margin="206,134,0,0" verticalalignment="top"/> </grid>
so can see that, though interface on observablebutton
, observabletogglebutton
types still routing change notification through parent, binding object happy because toe line on type.
is there reason why the child object needs implement interface though there need complete binding without it?
i try provide clear example how should done in wpf instead of trying fix op question.
xaml
<stackpanel> <stackpanel.resources> <booleantovisibilityconverter x:key="btov" /> </stackpanel.resources> <!--bind text viewmodel content. use bool visibilty converter convert true visible--> <textblock text="{binding path=content}" visibility="{binding path=iscontentvisible, converter={staticresource btov}}" /> <!--use 2 way binding sync ischecked property viewmodel--> <togglebutton ischecked="{binding path=iscontentvisible,mode=twoway}" content="{binding path=toogleactionname}" /> </stackpanel>
code behind
to keep project structure clear warmly suggest put each class in separate file. put 3 classes 1 single file easier posting.
using system.componentmodel; using system.runtime.compilerservices; using system.windows; namespace wpfapplication4 { /// <summary> /// interaction logic mainwindow.xaml /// </summary> public partial class mainwindow : window { public mainwindow() { initializecomponent(); datacontext = new contentviewmodel() { content = "foo" }; } } public class contentviewmodel : viewmodelbase { private string _toogleactionname = "turn off"; private bool _iscontentvisible = true; private string _content; public bool iscontentvisible { { return _iscontentvisible; } set { _iscontentvisible = value; //switch action name if (value) toogleactionname = "turn off"; else toogleactionname = "turn on"; onpropertychanged(); } } public string content { { return _content; } set { _content = value; onpropertychanged(); } } public string toogleactionname { { return _toogleactionname; } set { _toogleactionname = value; onpropertychanged(); } } } public class viewmodelbase : inotifypropertychanged { public event propertychangedeventhandler propertychanged; protected virtual void onpropertychanged([callermembername] string propertyname = null) { if (propertychanged != null) { propertychanged(this, new propertychangedeventargs(propertyname)); } } } }
i hope showing how wpf supposed work mvvm pattern.
Comments
Post a Comment