From: jp Date: Mon, 14 Mar 2016 15:17:57 +0000 (+0100) Subject: refactor bindings to allow 2 way bindings X-Git-Tag: v0.4~74^2^2~6 X-Git-Url: https://git.osiis.dedyn.io/?a=commitdiff_plain;h=e21653876231f1ad18d44b1953582a5a87c9e1d6;p=jp%2Fcrow.git refactor bindings to allow 2 way bindings --- diff --git a/Crow.csproj b/Crow.csproj index f0d0aa63..baa5b7a9 100644 --- a/Crow.csproj +++ b/Crow.csproj @@ -21,7 +21,6 @@ OnBuildSuccess v4.5 AnyCPU - 0.4 true @@ -147,6 +146,7 @@ + diff --git a/src/CompilerServices/CompilerServices.cs b/src/CompilerServices/CompilerServices.cs index da5c0fe4..cd491c4d 100644 --- a/src/CompilerServices/CompilerServices.cs +++ b/src/CompilerServices/CompilerServices.cs @@ -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 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> resolved = new Dictionary>(); + + 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 bindings = null; + if (!resolved.TryGetValue (b.Source.Instance, out bindings)) { + bindings = new List (); + 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 groupedByTarget = resolved.GroupBy (g => g.Target.Instance, g => g, (k, g) => g.ToArray ()); + foreach (List 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}); + } + } + + /// + /// Compile events expression in GOML attributes + /// + /// Event binding details + 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 ) diff --git a/src/GraphicObjects/GraphicObject.cs b/src/GraphicObjects/GraphicObject.cs index cddc918a..b61ecb32 100644 --- a/src/GraphicObjects/GraphicObject.cs +++ b/src/GraphicObjects/GraphicObject.cs @@ -1055,342 +1055,10 @@ namespace Crow #if DEBUG_BINDING Debug.WriteLine ("Resolve Bindings => " + this.ToString ()); #endif - //grouped bindings by Instance of Source - Dictionary> resolved = new Dictionary>(); - 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 bindings = null; - if (!resolved.TryGetValue (b.Source.Instance, out bindings)) { - bindings = new List (); - 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 groupedByTarget = resolved.GroupBy (g => g.Target.Instance, g => g, (k, g) => g.ToArray ()); - foreach (List 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); } - /// - /// Compile events expression in GOML attributes - /// - /// Event binding details - 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; - } /// /// 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 index 00000000..642c1e9e --- /dev/null +++ b/src/GraphicObjects/IBindable.cs @@ -0,0 +1,31 @@ +// +// IValueChange.cs +// +// Author: +// Jean-Philippe Bruyère +// +// 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 . +using System; +using System.Collections.Generic; + +namespace Crow +{ + public interface IBindable + { + List Bindings { get; } + } +} +