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 :

enter image description here

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

Popular posts from this blog

c# - How Configure Devart dotConnect for SQLite Code First? -

java - Copying object fields -

c++ - Clear the memory after returning a vector in a function -