]> O.S.I.I.S - jp/crow.git/commitdiff
refactor bindings to allow 2 way bindings
authorjp <jp_bruyere@hotmail.com>
Mon, 14 Mar 2016 15:17:57 +0000 (16:17 +0100)
committerjp <jp_bruyere@hotmail.com>
Mon, 14 Mar 2016 15:17:57 +0000 (16:17 +0100)
Crow.csproj
src/CompilerServices/CompilerServices.cs
src/GraphicObjects/GraphicObject.cs
src/GraphicObjects/IBindable.cs [new file with mode: 0644]

index f0d0aa632f0d507a0c569c2b4ab87d12a2855be6..baa5b7a903bfa51be3ff4e81dcd06433b533a38c 100644 (file)
@@ -21,7 +21,6 @@
     <RunPostBuildEvent>OnBuildSuccess</RunPostBuildEvent>
     <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
     <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
-    <ReleaseVersion>0.4</ReleaseVersion>
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
     <DebugSymbols>true</DebugSymbols>
     <Compile Include="src\rsvg\RsvgSharp.SizeFuncNative.cs" />
     <Compile Include="src\rsvg\SizeFunc.cs" />
     <Compile Include="src\MouseCursorChangedEventArgs.cs" />
+    <Compile Include="src\GraphicObjects\IBindable.cs" />
   </ItemGroup>
   <ItemGroup>
     <Reference Include="System" />
index da5c0fe48b1f4e251460ac50c7e79176fb02f43b..cd491c4dcb16d83c2afbdaeb49d5d6b31368ec7d 100644 (file)
@@ -50,6 +50,8 @@ namespace Crow
                string dynMethodId = "";
                bool resolved = false;
 
+               public bool TwoWayBinding = false;
+
                public string NewDynMethodId {
                        get {
                                if (!string.IsNullOrEmpty (dynMethodId))
@@ -88,19 +90,32 @@ namespace Crow
 
                #region CTOR
                public Binding(){}
-               public Binding(MemberReference _source, string _expression)
+               public Binding(MemberReference _target, string _expression)
                {
-                       Target = _source;
+                       Target = _target;
                        Expression = _expression;
                }
-               public Binding(object _source, string _member, string _expression)
+               public Binding(object _target, string _member, string _expression)
                {
-                       Target = new MemberReference (_source, _source.GetType().GetMember (_member) [0]);
+                       Target = new MemberReference (_target, _target.GetType().GetMember (_member) [0]);
                        Expression = _expression;
                }
+               public Binding(object _target, string _targetMember, object _source, string _sourceMember)
+               {
+                       Target = new MemberReference (_target, _target.GetType().GetMember (_targetMember) [0]);
+                       Source = new MemberReference (_source, _source.GetType().GetMember (_sourceMember) [0]);
+               }
+               public Binding(MemberReference _target, MemberReference _source)
+               {
+                       Target = _target;
+                       Source = _source;
+               }
                #endregion
 
-               public bool FindTarget(){
+               public bool FindSource(){
+                       if (Source != null)
+                               return true;
+                       
                        string member = null;
 
                        //if binding exp = '{}' => binding is done on datasource
@@ -112,7 +127,14 @@ namespace Crow
                                return true;
                        }
 
-                       string[] bindingExp = Expression.Split ('/');
+                       string expression = Expression;
+
+                       if (Expression.StartsWith ("²")) {
+                               expression = expression.Substring (1);
+                               TwoWayBinding = true;
+                       }
+
+                       string[] bindingExp = expression.Split ('/');
 
                        if (bindingExp.Length == 1) {
                                //datasource binding
@@ -164,10 +186,18 @@ namespace Crow
                                return false;
                        }
 
-                       if (Source.FindMember (member))
+                       if (Source.FindMember (member)) {
+                               if (TwoWayBinding) {
+                                       IBindable source = Source.Instance as IBindable;
+                                       if (source == null)
+                                               throw new Exception (Target.Instance.ToString() + " does not implement IBindable for 2 way bindings");
+                                       source.Bindings.Add (new Binding (Source, Target));
+                               }
                                return true;
+                       }
                        
                        Debug.WriteLine ("Binding member not found: " + member);
+                       Source = null;
                        return false;
                }
                public void Reset()
@@ -186,6 +216,351 @@ namespace Crow
 
        public static class CompilerServices
        {
+               public static void ResolveBindings(List<Binding> Bindings)
+               {
+                       if (Bindings == null)
+                               return;
+                       if (Bindings.Count == 0)
+                               return;
+                       #if DEBUG_BINDING
+                       Debug.WriteLine ("Resolve Bindings => " + this.ToString ());
+                       #endif
+                       //grouped bindings by Instance of Source
+                       Dictionary<object,List<Binding>> resolved = new Dictionary<object, List<Binding>>();
+
+                       foreach (Binding b in Bindings) {
+                               if (b.Resolved)
+                                       continue;
+                               if (b.Target.Member.MemberType == MemberTypes.Event) {
+                                       if (b.Expression.StartsWith("{")){
+                                               CompilerServices.CompileEventSource(b);
+                                               continue;
+                                       }
+                                       if (!b.FindSource ())
+                                               continue;
+                                       //register handler for event
+                                       if (b.Source.Method == null) {
+                                               Debug.WriteLine ("\tError: Handler Method not found: " + b.ToString());
+                                               continue;
+                                       }
+                                       try {
+                                               MethodInfo addHandler = b.Target.Event.GetAddMethod ();
+                                               Delegate del = Delegate.CreateDelegate (b.Target.Event.EventHandlerType, b.Source.Instance, b.Source.Method);
+                                               addHandler.Invoke (b.Target.Instance, new object[] { del });
+
+                                               #if DEBUG_BINDING
+                                               Debug.WriteLine ("\tHandler binded => " + b.ToString());
+                                               #endif
+                                               b.Resolved = true;
+                                       } catch (Exception ex) {
+                                               Debug.WriteLine ("\tERROR: " + ex.ToString());
+                                       }
+                                       continue;
+                               }
+
+                               if (!b.FindSource ())
+                                       continue;
+
+                               List<Binding> bindings = null;
+                               if (!resolved.TryGetValue (b.Source.Instance, out bindings)) {
+                                       bindings = new List<Binding> ();
+                                       resolved [b.Source.Instance] = bindings;
+                               }
+                               bindings.Add (b);
+                               b.Resolved = true;
+                       }
+
+                       MethodInfo stringEquals = typeof(string).GetMethod
+                               ("Equals", new Type[3] {typeof(string), typeof(string), typeof(StringComparison)});
+                       Type target_Type = Bindings[0].Target.Instance.GetType();
+                       EventInfo ei = typeof(IValueChange).GetEvent("ValueChanged");
+                       MethodInfo evtInvoke = ei.EventHandlerType.GetMethod ("Invoke");
+                       ParameterInfo[] evtParams = evtInvoke.GetParameters ();
+                       Type handlerArgsType = evtParams [1].ParameterType;
+                       Type[] args = {typeof(object), typeof(object),handlerArgsType};
+                       FieldInfo fiNewValue = typeof(ValueChangeEventArgs).GetField("NewValue");
+                       FieldInfo fiMbName = typeof(ValueChangeEventArgs).GetField("MemberName");
+
+                       //group;only one dynMethods by target (valuechanged event source)
+                       //changed value name tested in switch
+                       //IEnumerable<Binding[]> groupedByTarget = resolved.GroupBy (g => g.Target.Instance, g => g, (k, g) => g.ToArray ());
+                       foreach (List<Binding> grouped in resolved.Values) {
+                               int i = 0;
+                               Type source_Type = grouped[0].Source.Instance.GetType();
+
+                               DynamicMethod dm = null;
+                               ILGenerator il = null;
+
+                               System.Reflection.Emit.Label[] jumpTable = null;
+                               System.Reflection.Emit.Label endMethod = new System.Reflection.Emit.Label();
+
+                               #region Retrieve EventHandler parameter type
+                               //EventInfo ei = targetType.GetEvent ("ValueChanged");
+                               //no dynamic update if ValueChanged interface is not implemented
+                               if (source_Type.GetInterfaces().Contains(typeof(IValueChange))){
+                                       dm = new DynamicMethod(grouped[0].NewDynMethodId,
+                                               MethodAttributes.Family | MethodAttributes.FamANDAssem | MethodAttributes.NewSlot,
+                                               CallingConventions.Standard,
+                                               typeof(void),
+                                               args,
+                                               target_Type,true);
+
+                                       il = dm.GetILGenerator(256);
+
+                                       endMethod = il.DefineLabel();
+                                       jumpTable = new System.Reflection.Emit.Label[grouped.Count];
+                                       for (i = 0; i < grouped.Count; i++)
+                                               jumpTable [i] = il.DefineLabel ();
+                                       il.DeclareLocal(typeof(string));
+                                       il.DeclareLocal(typeof(object));
+
+                                       il.Emit(OpCodes.Nop);
+                                       il.Emit(OpCodes.Ldarg_0);
+                                       //il.Emit(OpCodes.Isinst, sourceType);
+                                       //push new value onto stack
+                                       il.Emit(OpCodes.Ldarg_2);
+                                       il.Emit(OpCodes.Ldfld, fiNewValue);
+                                       il.Emit(OpCodes.Stloc_1);
+                                       //push name
+                                       il.Emit(OpCodes.Ldarg_2);
+                                       il.Emit(OpCodes.Ldfld, fiMbName);
+                                       il.Emit(OpCodes.Stloc_0);
+                                       il.Emit(OpCodes.Ldloc_0);
+                                       il.Emit(OpCodes.Brfalse, endMethod);
+                               }
+                               #endregion
+
+                               i = 0;
+                               foreach (Binding b in grouped) {
+                                       #region initialize target with actual value
+                                       object targetValue = null;
+                                       if (b.Source.Member != null){
+                                               if (b.Source.Member.MemberType == MemberTypes.Property)
+                                                       targetValue = b.Source.Property.GetGetMethod ().Invoke (b.Source.Instance, null);
+                                               else if (b.Source.Member.MemberType == MemberTypes.Field)
+                                                       targetValue = b.Source.Field.GetValue (b.Source.Instance);
+                                               else if (b.Source.Member.MemberType == MemberTypes.Method){
+                                                       MethodInfo mthSrc = b.Source.Method;
+                                                       if (mthSrc.IsDefined(typeof(ExtensionAttribute), false))
+                                                               targetValue = mthSrc.Invoke(null, new object[] {b.Source.Instance});
+                                                       else
+                                                               targetValue = mthSrc.Invoke(b.Source.Instance, null);
+                                               }else
+                                                       throw new Exception ("unandled source member type for binding");
+                                       }else if (string.IsNullOrEmpty(b.Expression))
+                                               targetValue= grouped [0].Source.Instance;//empty binding exp=> bound to target object by default
+                                       //TODO: handle other dest type conversions
+                                       if (b.Target.Property.PropertyType == typeof(string)){
+                                               if (targetValue == null){
+                                                       //set default value
+
+                                               }else
+                                                       targetValue = targetValue.ToString ();
+                                       }
+                                       if (targetValue != null)
+                                               b.Target.Property.GetSetMethod ().Invoke
+                                               (b.Target.Instance, new object[] { b.Target.Property.PropertyType.Cast(targetValue)});
+                                       else
+                                               b.Target.Property.GetSetMethod ().Invoke
+                                               (b.Target.Instance, new object[] { targetValue });
+                                       #endregion
+
+                                       //if no dyn update, skip jump table
+                                       if (il == null)
+                                               continue;
+
+                                       il.Emit (OpCodes.Ldloc_0);
+                                       if (b.Source.Member != null)
+                                               il.Emit (OpCodes.Ldstr, b.Source.Member.Name);
+                                       else
+                                               il.Emit (OpCodes.Ldstr, b.Expression.Split('/').LastOrDefault());
+                                       il.Emit (OpCodes.Ldc_I4_4);//StringComparison.Ordinal
+                                       il.Emit (OpCodes.Callvirt, stringEquals);
+                                       il.Emit (OpCodes.Brtrue, jumpTable[i]);
+                                       i++;
+                               }
+
+                               if (il == null)
+                                       continue;
+
+                               il.Emit (OpCodes.Br, endMethod);
+
+                               i = 0;
+                               foreach (Binding b in grouped) {
+
+                                       il.MarkLabel (jumpTable [i]);
+                                       il.Emit(OpCodes.Ldloc_1);
+
+                                       //by default, target value type is deducted from source member type to allow
+                                       //memberless binding, if targetMember exists, it will be used to determine target
+                                       //value type for conversion
+                                       Type targetValueType = b.Target.Property.PropertyType;
+                                       if (b.Source.Member != null) {
+                                               if (b.Source.Member.MemberType == MemberTypes.Property)
+                                                       targetValueType = b.Source.Property.PropertyType;
+                                               else if (b.Source.Member.MemberType == MemberTypes.Field)
+                                                       targetValueType = b.Source.Field.FieldType;
+                                               else
+                                                       throw new Exception ("unhandle target member type in binding");
+                                       }
+
+                                       if (b.Target.Property.PropertyType == typeof(string)) {
+                                               MemberReference tostring = new MemberReference (b.Target.Instance);
+                                               if (!tostring.FindMember ("ToString"))
+                                                       throw new Exception ("ToString method not found");
+                                               il.Emit (OpCodes.Callvirt, tostring.Method);
+                                       }else if (!targetValueType.IsValueType)
+                                               il.Emit(OpCodes.Castclass, targetValueType);
+                                       else if (b.Target.Property.PropertyType != targetValueType)
+                                               il.Emit(OpCodes.Callvirt, CompilerServices.GetConvertMethod( b.Target.Property.PropertyType ));
+                                       else
+                                               il.Emit(OpCodes.Unbox_Any, b.Target.Property.PropertyType);
+
+                                       il.Emit(OpCodes.Callvirt, b.Target.Property.GetSetMethod());
+                                       il.Emit (OpCodes.Br, endMethod);
+                                       i++;
+
+                               }
+                               il.MarkLabel(endMethod);
+                               il.Emit(OpCodes.Pop);
+                               il.Emit(OpCodes.Ret);
+
+                               Delegate del = dm.CreateDelegate(ei.EventHandlerType, Bindings[0].Target.Instance);
+                               MethodInfo addHandler = ei.GetAddMethod ();
+                               addHandler.Invoke(grouped [0].Source.Instance, new object[] {del});
+                       }
+               }
+
+               /// <summary>
+               /// Compile events expression in GOML attributes
+               /// </summary>
+               /// <param name="es">Event binding details</param>
+               public static void CompileEventSource(Binding binding)
+               {
+                       #if DEBUG_BINDING
+                       Debug.WriteLine ("\tCompile Event Source => " + binding.ToString());
+                       #endif
+
+                       Type target_type = binding.Target.Instance.GetType();
+
+                       #region Retrieve EventHandler parameter type
+                       MethodInfo evtInvoke = binding.Target.Event.EventHandlerType.GetMethod ("Invoke");
+                       ParameterInfo[] evtParams = evtInvoke.GetParameters ();
+                       Type handlerArgsType = evtParams [1].ParameterType;
+                       #endregion
+
+                       Type[] args = {typeof(object), typeof(object),handlerArgsType};
+                       DynamicMethod dm = new DynamicMethod(binding.NewDynMethodId,
+                               typeof(void),
+                               args,
+                               target_type);
+
+
+                       #region IL generation
+                       ILGenerator il = dm.GetILGenerator(256);
+
+                       string src = binding.Expression.Trim();
+
+                       if (! (src.StartsWith("{") || src.EndsWith ("}")))
+                               throw new Exception (string.Format("GOML:Malformed {0} Event handler: {1}", binding.Target.Member.Name, binding.Expression));
+
+                       src = src.Substring (1, src.Length - 2);
+                       string[] srcLines = src.Split (new char[] { ';' });
+
+                       foreach (string srcLine in srcLines) {
+                               string statement = srcLine.Trim ();
+
+                               string[] operandes = statement.Split (new char[] { '=' });
+                               if (operandes.Length < 2) //not an affectation
+                               {
+                                       continue;
+                               }
+                               string lop = operandes [0].Trim ();
+                               string rop = operandes [operandes.Length-1].Trim ();
+
+                               #region LEFT OPERANDES
+                               GraphicObject lopObj = binding.Target.Instance as GraphicObject;        //default left operand base object is
+                               //the first arg (object sender) of the event handler
+
+                               il.Emit(OpCodes.Ldarg_0);       //load sender ref onto the stack
+
+                               string[] lopParts = lop.Split (new char[] { '.' });
+                               if (lopParts.Length > 1) {//should search also for member of es.Source
+                                       MethodInfo FindByNameMi = typeof(GraphicObject).GetMethod("FindByName");
+                                       for (int j = 0; j < lopParts.Length - 1; j++) {
+                                               il.Emit (OpCodes.Ldstr, lopParts[j]);
+                                               il.Emit(OpCodes.Callvirt, FindByNameMi);
+                                       }
+                               }
+
+                               int i = lopParts.Length -1;
+
+                               MemberInfo[] lopMbis = lopObj.GetType().GetMember (lopParts[i]);
+
+                               if (lopMbis.Length<1)
+                                       throw new Exception (string.Format("CROW BINDING: Member not found '{0}'", lop));
+
+                               OpCode lopSetOC;
+                               dynamic lopSetMbi;
+                               Type lopT = null;
+                               switch (lopMbis[0].MemberType) {
+                               case MemberTypes.Property:
+                                       PropertyInfo lopPi = target_type.GetProperty (lopParts[i]);
+                                       MethodInfo dstMi = lopPi.GetSetMethod ();
+                                       lopT = lopPi.PropertyType;
+                                       lopSetMbi = dstMi;
+                                       lopSetOC = OpCodes.Callvirt;
+                                       break;
+                               case MemberTypes.Field:
+                                       FieldInfo dstFi = target_type.GetField(lopParts[i]);
+                                       lopT = dstFi.FieldType;
+                                       lopSetMbi = dstFi;
+                                       lopSetOC = OpCodes.Stfld;
+                                       break;
+                               default:
+                                       throw new Exception (string.Format("GOML:member type not handle: {0}", lopParts[i]));
+                               }
+                               #endregion
+
+                               #region RIGHT OPERANDES
+                               if (rop.StartsWith("\'")){
+                                       if (!rop.EndsWith("\'"))
+                                               throw new Exception (string.Format
+                                                       ("GOML:malformed string constant in handler: {0}", rop));
+                                       string strcst = rop.Substring (1, rop.Length - 2);
+
+                                       il.Emit(OpCodes.Ldstr,strcst);
+
+                               }else{
+                                       if (lopT.IsEnum)
+                                               throw new NotImplementedException();
+
+                                       MethodInfo lopParseMi = lopT.GetMethod("Parse");
+                                       if (lopParseMi == null)
+                                               throw new Exception (string.Format
+                                                       ("GOML:no parse method found in: {0}", lopT.Name));
+                                       il.Emit(OpCodes.Ldstr, rop);
+                                       il.Emit(OpCodes.Callvirt, lopParseMi);
+                                       il.Emit(OpCodes.Unbox_Any, lopT);
+                               }
+
+                               #endregion
+
+                               //emit left operand assignment
+                               il.Emit(lopSetOC, lopSetMbi);
+                       }
+
+                       il.Emit(OpCodes.Ret);
+
+                       #endregion
+
+                       Delegate del = dm.CreateDelegate(binding.Target.Event.EventHandlerType, binding.Target.Instance);
+                       MethodInfo addHandler = binding.Target.Event.GetAddMethod ();
+                       addHandler.Invoke(binding.Target.Instance, new object[] {del});
+
+                       binding.Resolved = true;
+               }
+
                #region conversions
 
                internal static MethodInfo GetConvertMethod( Type targetType )
index cddc918a54afe7a857e83e3eaf24d1f758d5ba6f..b61ecb326bbe976f5d7dc148e0bda0fb03759a04 100644 (file)
@@ -1055,342 +1055,10 @@ namespace Crow
                        #if DEBUG_BINDING
                        Debug.WriteLine ("Resolve Bindings => " + this.ToString ());
                        #endif
-                       //grouped bindings by Instance of Source
-                       Dictionary<object,List<Binding>> resolved = new Dictionary<object, List<Binding>>();
 
-                       foreach (Binding b in Bindings) {
-                               if (b.Resolved)
-                                       continue;
-
-                               if (b.Target.Member.MemberType == MemberTypes.Event) {
-                                       if (b.Expression.StartsWith("{")){
-                                               CompileEventSource(b);
-                                               continue;
-                                       }
-                                       if (!b.FindTarget ())
-                                               continue;
-                                       //register handler for event
-                                       if (b.Source.Method == null) {
-                                               Debug.WriteLine ("\tError: Handler Method not found: " + b.ToString());
-                                               continue;
-                                       }
-                                       try {
-                                               MethodInfo addHandler = b.Target.Event.GetAddMethod ();
-                                               Delegate del = Delegate.CreateDelegate (b.Target.Event.EventHandlerType, b.Source.Instance, b.Source.Method);
-                                               addHandler.Invoke (this, new object[] { del });
-
-                                               #if DEBUG_BINDING
-                                               Debug.WriteLine ("\tHandler binded => " + b.ToString());
-                                               #endif
-                                               b.Resolved = true;
-                                       } catch (Exception ex) {
-                                               Debug.WriteLine ("\tERROR: " + ex.ToString());
-                                       }
-                                       continue;
-                               }
-
-                               if (!b.FindTarget ())
-                                       continue;
-
-                               List<Binding> bindings = null;
-                               if (!resolved.TryGetValue (b.Source.Instance, out bindings)) {
-                                       bindings = new List<Binding> ();
-                                       resolved [b.Source.Instance] = bindings;
-                               }
-                               bindings.Add (b);
-                               b.Resolved = true;
-
-                       }
-
-                       MethodInfo stringEquals = typeof(string).GetMethod
-                               ("Equals", new Type[3] {typeof(string), typeof(string), typeof(StringComparison)});
-                       Type sourceType = this.GetType();
-                       EventInfo ei = typeof(IValueChange).GetEvent("ValueChanged");
-                       MethodInfo evtInvoke = ei.EventHandlerType.GetMethod ("Invoke");
-                       ParameterInfo[] evtParams = evtInvoke.GetParameters ();
-                       Type handlerArgsType = evtParams [1].ParameterType;
-                       Type[] args = {typeof(object), typeof(object),handlerArgsType};
-                       FieldInfo fiNewValue = typeof(ValueChangeEventArgs).GetField("NewValue");
-                       FieldInfo fiMbName = typeof(ValueChangeEventArgs).GetField("MemberName");
-
-                       //group;only one dynMethods by target (valuechanged event source)
-                       //changed value name tested in switch
-                       //IEnumerable<Binding[]> groupedByTarget = resolved.GroupBy (g => g.Target.Instance, g => g, (k, g) => g.ToArray ());
-                       foreach (List<Binding> grouped in resolved.Values) {
-                               int i = 0;
-                               Type targetType = grouped[0].Source.Instance.GetType();
-
-                               DynamicMethod dm = null;
-                               ILGenerator il = null;
-
-                               System.Reflection.Emit.Label[] jumpTable = null;
-                               System.Reflection.Emit.Label endMethod = new System.Reflection.Emit.Label();
-
-                               #region Retrieve EventHandler parameter type
-                               //EventInfo ei = targetType.GetEvent ("ValueChanged");
-                               //no dynamic update if ValueChanged interface is not implemented
-                               if (targetType.GetInterfaces().Contains(typeof(IValueChange))){
-                                       dm = new DynamicMethod(grouped[0].NewDynMethodId,
-                                               MethodAttributes.Family | MethodAttributes.FamANDAssem | MethodAttributes.NewSlot,
-                                               CallingConventions.Standard,
-                                               typeof(void),
-                                               args,
-                                               sourceType,true);
-
-                                       il = dm.GetILGenerator(256);
-
-                                       endMethod = il.DefineLabel();
-                                       jumpTable = new System.Reflection.Emit.Label[grouped.Count];
-                                       for (i = 0; i < grouped.Count; i++)
-                                               jumpTable [i] = il.DefineLabel ();
-                                       il.DeclareLocal(typeof(string));
-                                       il.DeclareLocal(typeof(object));
-
-                                       il.Emit(OpCodes.Nop);
-                                       il.Emit(OpCodes.Ldarg_0);
-                                       //il.Emit(OpCodes.Isinst, sourceType);
-                                       //push new value onto stack
-                                       il.Emit(OpCodes.Ldarg_2);
-                                       il.Emit(OpCodes.Ldfld, fiNewValue);
-                                       il.Emit(OpCodes.Stloc_1);
-                                       //push name
-                                       il.Emit(OpCodes.Ldarg_2);
-                                       il.Emit(OpCodes.Ldfld, fiMbName);
-                                       il.Emit(OpCodes.Stloc_0);
-                                       il.Emit(OpCodes.Ldloc_0);
-                                       il.Emit(OpCodes.Brfalse, endMethod);
-                               }
-                               #endregion
-
-                               i = 0;
-                               foreach (Binding b in grouped) {
-                                       #region initialize target with actual value
-                                       object targetValue = null;
-                                       if (b.Source.Member != null){
-                                               if (b.Source.Member.MemberType == MemberTypes.Property)
-                                                       targetValue = b.Source.Property.GetGetMethod ().Invoke (b.Source.Instance, null);
-                                               else if (b.Source.Member.MemberType == MemberTypes.Field)
-                                                       targetValue = b.Source.Field.GetValue (b.Source.Instance);
-                                               else if (b.Source.Member.MemberType == MemberTypes.Method){
-                                                       MethodInfo mthSrc = b.Source.Method;
-                                                       if (mthSrc.IsDefined(typeof(ExtensionAttribute), false))
-                                                               targetValue = mthSrc.Invoke(null, new object[] {b.Source.Instance});
-                                                       else
-                                                               targetValue = mthSrc.Invoke(b.Source.Instance, null);
-                                               }else
-                                                       throw new Exception ("unandled source member type for binding");
-                                       }else if (string.IsNullOrEmpty(b.Expression))
-                                               targetValue= grouped [0].Source.Instance;//empty binding exp=> bound to target object by default
-                                       //TODO: handle other dest type conversions
-                                       if (b.Target.Property.PropertyType == typeof(string)){
-                                               if (targetValue == null){
-                                                       //set default value
-
-                                               }else
-                                                       targetValue = targetValue.ToString ();
-                                       }
-                                       if (targetValue != null)
-                                               b.Target.Property.GetSetMethod ().Invoke
-                                               (this, new object[] { b.Target.Property.PropertyType.Cast(targetValue)});
-                                       else
-                                               b.Target.Property.GetSetMethod ().Invoke
-                                               (this, new object[] { targetValue });
-                                       #endregion
-
-                                       //if no dyn update, skip jump table
-                                       if (il == null)
-                                               continue;
-
-                                       il.Emit (OpCodes.Ldloc_0);
-                                       if (b.Source.Member != null)
-                                               il.Emit (OpCodes.Ldstr, b.Source.Member.Name);
-                                       else
-                                               il.Emit (OpCodes.Ldstr, b.Expression.Split('/').LastOrDefault());
-                                       il.Emit (OpCodes.Ldc_I4_4);//StringComparison.Ordinal
-                                       il.Emit (OpCodes.Callvirt, stringEquals);
-                                       il.Emit (OpCodes.Brtrue, jumpTable[i]);
-                                       i++;
-                               }
-
-                               if (il == null)
-                                       continue;
-
-                               il.Emit (OpCodes.Br, endMethod);
-
-                               i = 0;
-                               foreach (Binding b in grouped) {
-
-                                       il.MarkLabel (jumpTable [i]);
-                                       il.Emit(OpCodes.Ldloc_1);
-
-                                       //by default, target value type is deducted from source member type to allow
-                                       //memberless binding, if targetMember exists, it will be used to determine target
-                                       //value type for conversion
-                                       Type targetValueType = b.Target.Property.PropertyType;
-                                       if (b.Source.Member != null) {
-                                               if (b.Source.Member.MemberType == MemberTypes.Property)
-                                                       targetValueType = b.Source.Property.PropertyType;
-                                               else if (b.Source.Member.MemberType == MemberTypes.Field)
-                                                       targetValueType = b.Source.Field.FieldType;
-                                               else
-                                                       throw new Exception ("unhandle target member type in binding");
-                                       }
-
-                                       if (b.Target.Property.PropertyType == typeof(string)) {
-                                               MemberReference tostring = new MemberReference (b.Target.Instance);
-                                               if (!tostring.FindMember ("ToString"))
-                                                       throw new Exception ("ToString method not found");
-                                               il.Emit (OpCodes.Callvirt, tostring.Method);
-                                       }else if (!targetValueType.IsValueType)
-                                               il.Emit(OpCodes.Castclass, targetValueType);
-                                       else if (b.Target.Property.PropertyType != targetValueType)
-                                               il.Emit(OpCodes.Callvirt, CompilerServices.GetConvertMethod( b.Target.Property.PropertyType ));
-                                       else
-                                               il.Emit(OpCodes.Unbox_Any, b.Target.Property.PropertyType);
-
-                                       il.Emit(OpCodes.Callvirt, b.Target.Property.GetSetMethod());
-                                       il.Emit (OpCodes.Br, endMethod);
-                                       i++;
-
-                               }
-                               il.MarkLabel(endMethod);
-                               il.Emit(OpCodes.Pop);
-                               il.Emit(OpCodes.Ret);
-
-                               Delegate del = dm.CreateDelegate(ei.EventHandlerType, this);
-                               MethodInfo addHandler = ei.GetAddMethod ();
-                               addHandler.Invoke(grouped [0].Source.Instance, new object[] {del});
-                       }
+                       CompilerServices.ResolveBindings (Bindings);
                }
-               /// <summary>
-               /// Compile events expression in GOML attributes
-               /// </summary>
-               /// <param name="es">Event binding details</param>
-               public void CompileEventSource(Binding binding)
-               {
-                       #if DEBUG_BINDING
-                       Debug.WriteLine ("\tCompile Event Source => " + binding.ToString());
-                       #endif
-
-                       Type sourceType = this.GetType();
-
-                       #region Retrieve EventHandler parameter type
-                       MethodInfo evtInvoke = binding.Target.Event.EventHandlerType.GetMethod ("Invoke");
-                       ParameterInfo[] evtParams = evtInvoke.GetParameters ();
-                       Type handlerArgsType = evtParams [1].ParameterType;
-                       #endregion
-
-                       Type[] args = {typeof(object), typeof(object),handlerArgsType};
-                       DynamicMethod dm = new DynamicMethod(binding.NewDynMethodId,
-                               typeof(void),
-                               args,
-                               sourceType);
-
-
-                       #region IL generation
-                       ILGenerator il = dm.GetILGenerator(256);
-
-                       string src = binding.Expression.Trim();
-
-                       if (! (src.StartsWith("{") || src.EndsWith ("}")))
-                               throw new Exception (string.Format("GOML:Malformed {0} Event handler: {1}", binding.Target.Member.Name, binding.Expression));
-
-                       src = src.Substring (1, src.Length - 2);
-                       string[] srcLines = src.Split (new char[] { ';' });
-
-                       foreach (string srcLine in srcLines) {
-                               string statement = srcLine.Trim ();
-
-                               string[] operandes = statement.Split (new char[] { '=' });
-                               if (operandes.Length < 2) //not an affectation
-                               {
-                                       continue;
-                               }
-                               string lop = operandes [0].Trim ();
-                               string rop = operandes [operandes.Length-1].Trim ();
-
-                               #region LEFT OPERANDES
-                               GraphicObject lopObj = this;    //default left operand base object is
-                               //the first arg (object sender) of the event handler
-
-                               il.Emit(OpCodes.Ldarg_0);       //load sender ref onto the stack
-
-                               string[] lopParts = lop.Split (new char[] { '.' });
-                               if (lopParts.Length > 1) {//should search also for member of es.Source
-                                       MethodInfo FindByNameMi = typeof(GraphicObject).GetMethod("FindByName");
-                                       for (int j = 0; j < lopParts.Length - 1; j++) {
-                                               il.Emit (OpCodes.Ldstr, lopParts[j]);
-                                               il.Emit(OpCodes.Callvirt, FindByNameMi);
-                                       }
-                               }
 
-                               int i = lopParts.Length -1;
-
-                               MemberInfo[] lopMbis = lopObj.GetType().GetMember (lopParts[i]);
-
-                               if (lopMbis.Length<1)
-                                       throw new Exception (string.Format("CROW BINDING: Member not found '{0}'", lop));
-
-                               OpCode lopSetOC;
-                               dynamic lopSetMbi;
-                               Type lopT = null;
-                               switch (lopMbis[0].MemberType) {
-                               case MemberTypes.Property:
-                                       PropertyInfo lopPi = sourceType.GetProperty (lopParts[i]);
-                                       MethodInfo dstMi = lopPi.GetSetMethod ();
-                                       lopT = lopPi.PropertyType;
-                                       lopSetMbi = dstMi;
-                                       lopSetOC = OpCodes.Callvirt;
-                                       break;
-                               case MemberTypes.Field:
-                                       FieldInfo dstFi = sourceType.GetField(lopParts[i]);
-                                       lopT = dstFi.FieldType;
-                                       lopSetMbi = dstFi;
-                                       lopSetOC = OpCodes.Stfld;
-                                       break;
-                               default:
-                                       throw new Exception (string.Format("GOML:member type not handle: {0}", lopParts[i]));
-                               }
-                               #endregion
-
-                               #region RIGHT OPERANDES
-                               if (rop.StartsWith("\'")){
-                                       if (!rop.EndsWith("\'"))
-                                               throw new Exception (string.Format
-                                                       ("GOML:malformed string constant in handler: {0}", rop));
-                                       string strcst = rop.Substring (1, rop.Length - 2);
-
-                                       il.Emit(OpCodes.Ldstr,strcst);
-
-                               }else{
-                                       if (lopT.IsEnum)
-                                               throw new NotImplementedException();
-
-                                       MethodInfo lopParseMi = lopT.GetMethod("Parse");
-                                       if (lopParseMi == null)
-                                               throw new Exception (string.Format
-                                                       ("GOML:no parse method found in: {0}", lopT.Name));
-                                       il.Emit(OpCodes.Ldstr, rop);
-                                       il.Emit(OpCodes.Callvirt, lopParseMi);
-                                       il.Emit(OpCodes.Unbox_Any, lopT);
-                               }
-
-                               #endregion
-
-                               //emit left operand assignment
-                               il.Emit(lopSetOC, lopSetMbi);
-                       }
-
-                       il.Emit(OpCodes.Ret);
-
-                       #endregion
-
-                       Delegate del = dm.CreateDelegate(binding.Target.Event.EventHandlerType,this);
-                       MethodInfo addHandler = binding.Target.Event.GetAddMethod ();
-                       addHandler.Invoke(this, new object[] {del});
-
-                       binding.Resolved = true;
-               }
                /// <summary>
                /// Remove dynamic delegates by ids from dataSource
                ///  and delete ref of this in Shared interface refs
diff --git a/src/GraphicObjects/IBindable.cs b/src/GraphicObjects/IBindable.cs
new file mode 100644 (file)
index 0000000..642c1e9
--- /dev/null
@@ -0,0 +1,31 @@
+//
+//  IValueChange.cs
+//
+//  Author:
+//       Jean-Philippe Bruyère <jp.bruyere@hotmail.com>
+//
+//  Copyright (c) 2015 jp
+//
+//  This program is free software: you can redistribute it and/or modify
+//  it under the terms of the GNU General Public License as published by
+//  the Free Software Foundation, either version 3 of the License, or
+//  (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+using System;
+using System.Collections.Generic;
+
+namespace Crow
+{
+       public interface IBindable
+       {
+               List<Binding> Bindings { get; }
+       }
+}
+