c# - How can I bind a model property to another property of the same instance of that model? -
tl;dr :
<usercontrol.datacontext> <foo:bar> <foo:bar.baz blat={binding source={relativesource self}, path=bing, mode=twoway}"/>
results in following error message :
- system.windows.data warning: 40 :
- bindingexpression path error: 'bing' property not found on 'object' 'relativesource' (hashcode=38995967)'.
- bindingexpression:path=bing; dataitem='relativesource' (hashcode=38995967);
- target element 'bar' (hashcode=53937671);
- target property 'baz' (type 'baz')`
and ends binding failing - why?
the meat of problem
i have rather convoluted colormodel
class in have inherited dependencyobject
inotifypropertychanged
:
[serializable] public class colormodel : dependencyobject, inotifypropertychanged { }
within class 4 double values - alpha
, red
, green
, blue
:
//the latter 3 dependencyproperties pretty same : public static readonly dependencyproperty alphaproperty = dependencyproperty.register( "alpha", typeof(double), typeof(colormodel), new propertymetadata(1.0d)); public double alpha { { return ( double )this.getvalue( alphaproperty ); } set { this.setvalue( alphaproperty, value ); this.t = task.whenall( new task[ ] { this.onpropertychanged( "alpha" ), this.onpropertychanged( "color" ) } ); } }
then there onpropertychanged method :
protected async task onpropertychanged( string v ) { if ( this.propertychanged != null ) await this.propertychanged.async( this, new propertychangedeventargs( v ) ).dontblock( ); }
and have color
property :
public static readonly dependencyproperty colorproperty = dependencyproperty.register( "color", typeof(color), typeof(colormodel), new propertymetadata(colors.black)); /// <summary> /// or set color property. /// </summary> public color color { { return ( color )this.getvalue( colorproperty ); } set { this.setvalue( colorproperty, value ); this.t = task.whenall( new task[ ] { this.onpropertychanged("alpha").dontblock( ), this.onpropertychanged("red").dontblock( ), this.onpropertychanged("green").dontblock( ), this.onpropertychanged("blue").dontblock( ), this.onpropertychanged("color").dontblock( ) } ).dontblock( ); } }
never-minding asynchronous shenanigans not relevant question, have constructed control supposed allow user control aspects of single color :
i have each of slide bars bound bidirectionally a/r/g/b properties of colormodel :
<slider value="{binding alpha, mode=twoway}" .../>
and finally, within controls xaml have defined datacontext :
<usercontrol.datacontext> <controls:colormodel> <controls:colormodel.color> <multibinding converter="{staticresource cmc}"> <binding path="alpha" mode="twoway" source="{relativesource self}"/> <binding path="red" mode="twoway" source="{relativesource self}"/> <binding path="green" mode="twoway" source="{relativesource self}"/> <binding path="blue" mode="twoway" source="{relativesource self}"/> </multibinding> </controls:colormodel.color> </controls:colormodel> </usercontrol.datacontext>
this converter using :
/// <summary> /// class converting colors to/from raw values. /// </summary> public class colormulticonverter : imultivalueconverter { /// <summary> /// convert incoming raw color values single color. /// </summary> /// <param name="values"> /// [0] : alpha /// [1] : red /// [2] : green /// [3] : blue /// </param> /// <param name="targettype">color</param> /// <param name="parameter"></param> /// <param name="culture"></param> /// <returns></returns> public object convert( object[ ] values, type targettype, object parameter, cultureinfo culture ) { return values.contains( dependencyproperty.unsetvalue ) ? dependencyproperty.unsetvalue : color.fromscrgb( doubletofloat( ( double )values[ 0 ] ), doubletofloat( ( double )values[ 1 ] ), doubletofloat( ( double )values[ 2 ] ), doubletofloat( ( double )values[ 3 ] ) ); } public object[ ] convertback( object value, type[ ] targettypes, object parameter, cultureinfo culture ) { if ( value == dependencyproperty.unsetvalue ) return new object[ ] { dependencyproperty.unsetvalue, dependencyproperty.unsetvalue, dependencyproperty.unsetvalue, dependencyproperty.unsetvalue }; color c = ( color )value; return new object[ ] { floattodouble( c.sca ), floattodouble( c.scr ), floattodouble( c.scg ), floattodouble( c.scb ) }; } private float doubletofloat( double d ) { return system.convert.tosingle( d ); } private double floattodouble( float f ) { return system.convert.todouble( f ); } }
and chagrin , utter lack of surprise, fails miserably. receive following error message(s) when load window control :
- system.windows.data warning: 40 :
- bindingexpression path error: 'alpha' property not found on 'object' ''relativesource' (hashcode=38995967)'. bindingexpression:path=alpha;
- dataitem='relativesource' (hashcode=38995967);
- target element 'colormodel' (hashcode=53937671);
- target property 'color' (type 'color')`
which, after longest ever of lead-ins, brings question - how can bind ( or multi-bind, or... whatever ) property of model property of same instance of model?
okay able find way but, @ least respects limited understanding, way able in constructor of colormodel.
in addition, added drop of code dependencyproperties of model.
public static readonly dependencyproperty colorproperty = dependencyproperty.register( "color", typeof( color ), typeof( colormodel ), new propertymetadata( colors.black, async ( s, e ) => await ( s colormodel ).onpropertychanged( "color" ).dontblock( ) ) ), alphaproperty = dependencyproperty.register( "alpha", typeof( double ), typeof( colormodel ), new propertymetadata( 1.0d, async ( s, e ) => await ( s colormodel ).onpropertychanged( "alpha" ).dontblock( ) ) );
these dependencyproperties trigger call underlying colormodels onpropertychanged
method bound through inotifypropertychanged
interface informed ( untested, dangerous, , lead catastrophic failure via flaming death-spiral of infinite flaming loops ).
and in constructor added following code :
public colormodel( ) { multibinding mb = new multibinding( ); mb.converter = new colormulticonverter( ); mb.bindings.add( new binding( "alpha" ) { source = this, mode = bindingmode.twoway } ); mb.bindings.add( new binding( "red" ) { source = this, mode = bindingmode.twoway } ); mb.bindings.add( new binding( "green" ) { source = this, mode = bindingmode.twoway } ); mb.bindings.add( new binding( "blue" ) { source = this, mode = bindingmode.twoway } ); bindingoperations.setbinding( this, colorproperty, mb ); }
this best solution come - according other answer, trying use relativesource
works visualtree
elements guess explains why failed when tried in xaml.
Comments
Post a Comment