wpf - Why Are ICommand Properties Treated Specially by Bindings? -
so far, had impression wpf looks @ actual type of object gets via binding or in other way determine templates, styles , representation use. however, confronted situation makes seem wpf (also?) looks @ declared property type reason.
this exemplary view model:
using system; using system.windows.input; public class simpleviewmodel { private class myexamplecommand : icommand { public bool canexecute(object parameter) { return true; } public event eventhandler canexecutechanged; public void execute(object parameter) { } public override string tostring() { return "test"; } } private icommand examplecommand; public icommand examplecommand { { if (examplecommand == null) { examplecommand = new myexamplecommand(); } return examplecommand; } } }
use instance of class data context in window , add button:
<button> <textblock text="{binding examplecommand}"/> </button>
in running application, button empty. if simpleviewmodel.examplecommand
typed object
instead of icommand
, test
shown label on button expected.
what wrong here? wpf treat objects differently based on declared type of property returned them? can worked around, , other types beside icommand
affected?
tostring()
declared on object
, icommand
not object
interface. assignable object
.
the binding system not, said, differentiate on declared type. default ivalueconverter
used in case of conversion string
does.
internally framework uses defaultvalueconverter
when no user defined converter given. in create
method can see why interfaces act differently objects here (look specific check of sourcetype.isinterface
):
internal static ivalueconverter create(type sourcetype, type targettype, bool targettosource, databindengine engine) { typeconverter typeconverter; type innertype; bool canconvertto, canconvertfrom; bool sourceisnullable = false; bool targetisnullable = false; // sometimes, no conversion necessary if (sourcetype == targettype || (!targettosource && targettype.isassignablefrom(sourcetype))) { return valueconverternotneeded; } // type convert system.object useless. claims can // convert string, throws exception when asked // so. work around it. if (targettype == typeof(object)) { // sourcetype here might nullable type: consider using // nullableconverter when appropriate. (uncomment following lines) //type innertype = nullable.getunderlyingtype(sourcetype); //if (innertype != null) //{ // return new nullableconverter(new objecttargetconverter(innertype), // innertype, targettype, true, false); //} // return new objecttargetconverter(sourcetype, engine); } else if (sourcetype == typeof(object)) { // targettype here might nullable type: consider using // nullableconverter when appropriate. (uncomment following lines) //type innertype = nullable.getunderlyingtype(targettype); // if (innertype != null) // { // return new nullableconverter(new objectsourceconverter(innertype), // sourcetype, innertype, false, true); // } // return new objectsourceconverter(targettype, engine); } // use system.convert well-known base types if (systemconvertconverter.canconvert(sourcetype, targettype)) { return new systemconvertconverter(sourcetype, targettype); } // need check nullable types first, since nullableconverter bit over-eager; // typeconverter nullable can convert e.g. nullable<datetime> string // ends doing different conversion typeconverter // generic's inner type, e.g. bug 1361977 innertype = nullable.getunderlyingtype(sourcetype); if (innertype != null) { sourcetype = innertype; sourceisnullable = true; } innertype = nullable.getunderlyingtype(targettype); if (innertype != null) { targettype = innertype; targetisnullable = true; } if (sourceisnullable || targetisnullable) { // single-level recursive call try find converter basic value types return create(sourcetype, targettype, targettosource, engine); } // special case converting ilistsource ilist if (typeof(ilistsource).isassignablefrom(sourcetype) && targettype.isassignablefrom(typeof(ilist))) { return new listsourceconverter(); } // interfaces best handled on per-instance basis. type may // not implement interface, instance of derived type may. if (sourcetype.isinterface || targettype.isinterface) { return new interfaceconverter(sourcetype, targettype); } // try using source's type converter typeconverter = getconverter(sourcetype); canconvertto = (typeconverter != null) ? typeconverter.canconvertto(targettype) : false; canconvertfrom = (typeconverter != null) ? typeconverter.canconvertfrom(targettype) : false; if ((canconvertto || targettype.isassignablefrom(sourcetype)) && (!targettosource || canconvertfrom || sourcetype.isassignablefrom(targettype))) { return new sourcedefaultvalueconverter(typeconverter, sourcetype, targettype, targettosource && canconvertfrom, canconvertto, engine); } // if doesn't work, try using target's type converter typeconverter = getconverter(targettype); canconvertto = (typeconverter != null) ? typeconverter.canconvertto(sourcetype) : false; canconvertfrom = (typeconverter != null) ? typeconverter.canconvertfrom(sourcetype) : false; if ((canconvertfrom || targettype.isassignablefrom(sourcetype)) && (!targettosource || canconvertto || sourcetype.isassignablefrom(targettype))) { return new targetdefaultvalueconverter(typeconverter, sourcetype, targettype, canconvertfrom, targettosource && canconvertto, engine); } // nothing worked, give return null; }
according documentation should provide user defined ivalueconverter
when binding property of different type dependency property binding relying on tostring
being called implementation detail of frameworks default conversion mechanism (and undocumented far know, states default , fallback values defined circumstances) , change @ moment.
Comments
Post a Comment