PropertyBinding
}
public class Binding{
- public BindingType BindingType;
- public string MemberName;
- public string BindingExpression;
+ public PropertyInfo Property;
+ public string Expression;
+
+ public Binding(){
+ }
+ public Binding(PropertyInfo _property, string _expression)
+ {
+ Property = _property;
+ Expression = _expression;
+ }
+ }
+
+ public class ResolvedBinding
+ {
+ public object Target;
+ public MemberInfo Member;
+
+ public Binding Binding;
+
+ public ResolvedBinding(){
+ }
+ public ResolvedBinding(Binding _binding){
+ Binding = _binding;
+ }
+
+ public bool FindTarget(GraphicObject source){
+ string member = null;
+
+ if (string.IsNullOrEmpty (Binding.Expression)) {
+ Target = source;
+ Member = null;
+ return true;
+ }
+
+ string[] bindingExp = Binding.Expression.Split ('/');
+
+ if (bindingExp.Length == 1) {
+ //datasource binding
+ Target = source.DataSource;
+ member = bindingExp [0];
+ } else {
+ int ptr = 0;
+ ILayoutable tmp = source;
+ if (string.IsNullOrEmpty (bindingExp [0])) {
+ //if exp start with '/' => Graphic tree parsing start at top container
+ tmp = source.TopContainer as ILayoutable;
+ ptr++;
+ }
+ while (ptr < bindingExp.Length - 1) {
+ if (tmp == null)
+ return false;
+ if (bindingExp [ptr] == "..")
+ tmp = tmp.Parent as GraphicObject;
+ else if (bindingExp [ptr] == ".") {
+ if (ptr > 0)
+ throw new Exception ("Syntax error in binding, './' may only appear in first position");
+ tmp = source;
+ }else
+ tmp = (tmp as GraphicObject).FindByName (bindingExp [ptr]);
+ ptr++;
+ }
+
+ string[] bindTrg = bindingExp [ptr].Split ('.');
+
+ if (bindTrg.Length == 2) {
+ tmp = (tmp as GraphicObject).FindByName (bindTrg [0]);
+ member = bindTrg [1];
+ } else
+ throw new Exception ("Syntax error in binding, expected 'go dot member'");
+ Target = tmp;
+ }
+ if (Target == null) {
+ Debug.WriteLine ("Binding Source is null: " + Binding.Expression);
+ return false;
+ }
+ Type targetType = Target.GetType ();
+ Member = targetType.GetMember (member).FirstOrDefault ();
+
+ #region search for extensions methods if member not found in type
+ if (Member == null && !string.IsNullOrEmpty(member))
+ {
+ Assembly a = Assembly.GetExecutingAssembly();
+ Member = CompilerServices.GetExtensionMethods(a, targetType).FirstOrDefault();
+ }
+ #endregion
+
+ if (member == null) {
+ Debug.WriteLine ("Binding member not found: " + member);
+ return false;
+ }
+
+ return true;
+ }
}
public static class CompilerServices2
}
public static class CompilerServices
{
+ public static void CreateBindingHandler()
+ {
+ }
+
static int dynHandleCpt = 0;
/// <summary>
using System.IO;\r
//using System.Xml;\r
using System.Xml;\r
+using System.Runtime.CompilerServices;\r
+using System.Reflection.Emit;\r
\r
namespace go\r
{ \r
public class GraphicObject : IXmlSerializable, ILayoutable, IValueChange\r
{\r
internal List<int> DynamicMethodIds = new List<int> ();\r
+ internal List<Binding> Bindings = new List<Binding> ();\r
\r
#region IValueChange implementation\r
public event EventHandler<ValueChangeEventArgs> ValueChanged;\r
\r
[XmlIgnore]public object DataSource {\r
set {\r
+ if (dataSource == value)\r
+ return;\r
+\r
+ if (dataSource != null)\r
+ this.ClearBinding ();\r
+ \r
dataSource = value;\r
+\r
+ if (dataSource != null)\r
+ this.ResolveBindings();\r
}\r
get { \r
return dataSource == null ? \r
tmp = Parent.ToString () + tmp;\r
return Name == "unamed" ? tmp + "." + this.GetType ().Name : tmp + "." + Name;\r
}\r
- \r
+\r
+ public virtual void ResolveBindings()\r
+ {\r
+ List<ResolvedBinding> resolved = new List<ResolvedBinding> ();\r
+ foreach (Binding b in Bindings) {\r
+ ResolvedBinding rb = new ResolvedBinding (b);\r
+ if (!rb.FindTarget (this))\r
+ continue;\r
+ resolved.Add (rb); \r
+ }\r
+ IEnumerable<ResolvedBinding[]> groupedByTarget = resolved.GroupBy (g => g.Target, g => g, (k, g) => g.ToArray ());\r
+ foreach (ResolvedBinding[] grouped in groupedByTarget) {\r
+ int i = 0;\r
+ object targetValue = grouped [0].Target;//empty binding exp=> bound to target object by default\r
+ Type targetType = targetValue.GetType();\r
+ Type sourceType = this.GetType();\r
+\r
+ DynamicMethod dm = null;\r
+ ILGenerator il = null;\r
+\r
+ MethodInfo stringEquals = typeof(string).GetMethod\r
+ ("op_Equality", new Type[2] {typeof(string), typeof(string)});\r
+\r
+\r
+ System.Reflection.Emit.Label[] jumpTable = null;\r
+ System.Reflection.Emit.Label endMethod = new System.Reflection.Emit.Label();\r
+\r
+ LocalBuilder lbMemberName = null;\r
+ LocalBuilder lbValue = null;\r
+\r
+ #region Retrieve EventHandler parameter type\r
+ EventInfo ei = targetType.GetEvent ("ValueChanged");\r
+ //no dynamic update if ValueChanged interface is not implemented\r
+ if (ei != null){\r
+ MethodInfo evtInvoke = ei.EventHandlerType.GetMethod ("Invoke");\r
+ ParameterInfo[] evtParams = evtInvoke.GetParameters ();\r
+ Type handlerArgsType = evtParams [1].ParameterType;\r
+\r
+ Type[] args = {typeof(object), typeof(object),handlerArgsType};\r
+ dm = new DynamicMethod("dynHandle",\r
+ typeof(void), \r
+ args,\r
+ sourceType);\r
+ \r
+\r
+ il = dm.GetILGenerator(256);\r
+\r
+\r
+ endMethod = il.DefineLabel();\r
+ jumpTable = new System.Reflection.Emit.Label[grouped.Length];\r
+ for (i = 0; i < grouped.Length; i++)\r
+ jumpTable [i] = il.DefineLabel ();\r
+ lbMemberName = il.DeclareLocal(typeof(string));\r
+ lbValue = il.DeclareLocal(typeof(object));\r
+\r
+ il.Emit(OpCodes.Nop);\r
+ il.Emit(OpCodes.Ldarg_0);\r
+ //il.Emit(OpCodes.Isinst, sourceType);\r
+ //push new value onto stack\r
+ il.Emit(OpCodes.Ldarg_2);\r
+ FieldInfo fiNewValue = typeof(ValueChangeEventArgs).GetField("NewValue");\r
+ il.Emit(OpCodes.Ldfld, fiNewValue);\r
+ il.Emit(OpCodes.Stloc, lbValue);\r
+ //push name \r
+ il.Emit(OpCodes.Ldarg_2);\r
+ FieldInfo fiMbName = typeof(ValueChangeEventArgs).GetField("MemberName");\r
+ il.Emit(OpCodes.Ldfld, fiMbName);\r
+ il.Emit(OpCodes.Stloc, lbMemberName);\r
+ il.Emit(OpCodes.Ldloc, lbMemberName);\r
+ il.Emit(OpCodes.Brfalse, endMethod);\r
+ }\r
+ #endregion\r
+\r
+ i = 0;\r
+ foreach (ResolvedBinding rb in grouped) {\r
+ #region initialize target with actual value\r
+ if (rb.Member != null){\r
+ if (rb.Member.MemberType == MemberTypes.Property)\r
+ targetValue = (rb.Member as PropertyInfo).GetGetMethod ().Invoke (rb.Target, null);\r
+ else if (rb.Member.MemberType == MemberTypes.Field)\r
+ targetValue = (rb.Member as FieldInfo).GetValue (rb.Target);\r
+ else if (rb.Member.MemberType == MemberTypes.Method){\r
+ MethodInfo mthSrc = rb.Member as MethodInfo;\r
+ if (mthSrc.IsDefined(typeof(ExtensionAttribute), false))\r
+ targetValue = mthSrc.Invoke(null, new object[] {rb.Target});\r
+ else\r
+ targetValue = mthSrc.Invoke(rb.Target, null);\r
+ }else\r
+ throw new Exception ("unandled source member type for binding"); \r
+ }\r
+ //TODO: handle other dest type conversions\r
+ if (rb.Binding.Property.PropertyType == typeof(string)){\r
+ if (targetValue != null)\r
+ targetValue = targetValue.ToString ();\r
+ }\r
+ rb.Binding.Property.GetSetMethod ().Invoke (this, new object[] { targetValue });\r
+ #endregion\r
+\r
+ //if no dyn update, skip jump table\r
+ if (il == null)\r
+ continue;\r
+\r
+ il.Emit (OpCodes.Ldloc, lbMemberName);\r
+ il.Emit (OpCodes.Ldstr, rb.Member.Name);\r
+ il.Emit (OpCodes.Callvirt, stringEquals);\r
+ il.Emit (OpCodes.Brtrue, jumpTable[i]);\r
+ i++;\r
+ }\r
+\r
+ if (il == null)\r
+ continue;\r
+ \r
+ il.Emit (OpCodes.Br, endMethod);\r
+\r
+ i = 0;\r
+ foreach (ResolvedBinding rb in grouped) {\r
+\r
+ il.MarkLabel (jumpTable [i]);\r
+ il.Emit(OpCodes.Ldloc, lbValue);\r
+\r
+ //by default, target value type is deducted from source member type to allow\r
+ //memberless binding, if targetMember exists, it will be used to determine target\r
+ //value type for conversion\r
+ Type targetValueType = rb.Binding.Property.PropertyType;\r
+ if (rb.Member != null) {\r
+ if (rb.Member.MemberType == MemberTypes.Property)\r
+ targetValueType = (rb.Member as PropertyInfo).PropertyType;\r
+ else if (rb.Member.MemberType == MemberTypes.Field)\r
+ targetValueType = (rb.Member as FieldInfo).FieldType;\r
+ else\r
+ throw new Exception ("unhandle target member type in binding");\r
+ }\r
+ \r
+ if (!targetValueType.IsValueType)\r
+ il.Emit(OpCodes.Castclass, targetValueType);\r
+ else if (rb.Binding.Property.PropertyType != targetValueType)\r
+ il.Emit(OpCodes.Callvirt, CompilerServices.GetConvertMethod( rb.Binding.Property.PropertyType ));\r
+ else\r
+ il.Emit(OpCodes.Unbox_Any, rb.Binding.Property.PropertyType);\r
+\r
+ il.Emit(OpCodes.Callvirt, rb.Binding.Property.GetSetMethod());\r
+ il.Emit (OpCodes.Br, endMethod);\r
+ i++;\r
+\r
+ }\r
+ il.MarkLabel(endMethod);\r
+ il.Emit(OpCodes.Pop);\r
+ il.Emit(OpCodes.Ret);\r
+\r
+ Delegate del = dm.CreateDelegate(ei.EventHandlerType, this);\r
+ MethodInfo addHandler = ei.GetAddMethod ();\r
+ addHandler.Invoke(grouped [0].Target, new object[] {del});\r
+ }\r
+ }\r
+\r
#region IXmlSerializable\r
public virtual System.Xml.Schema.XmlSchema GetSchema ()\r
{\r
// } else {\r
\r
if (attValue.StartsWith("{")) {\r
- if (Interface.DontResoveGOML)\r
- continue;\r
//binding\r
if (!attValue.EndsWith("}"))\r
throw new Exception (string.Format("GOML:Malformed binding: {0}", attValue));\r
\r
- string strBinding = attValue.Substring (1, attValue.Length - 2);\r
- Interface.GOMLResolver.Add (new DynAttribute () {\r
- Source = this,\r
- MemberName = attName,\r
- Value = strBinding\r
- });\r
+ this.Bindings.Add (new Binding (pi, attValue.Substring (1, attValue.Length - 2)));\r
continue;\r
}\r
\r
/// and delete ref of this in Shared interface refs\r
/// </summary>\r
public virtual void ClearBinding(){\r
- object ds = this.DataSource;\r
- if (ds != null) {\r
- Type dataSourceType = ds.GetType ();\r
- EventInfo evtInfo = dataSourceType.GetEvent ("ValueChanged");\r
- if (evtInfo != null) {\r
- FieldInfo evtFi = CompilerServices.GetEventHandlerField (dataSourceType, "ValueChanged");\r
- MulticastDelegate multicastDelegate = evtFi.GetValue (ds) as MulticastDelegate;\r
- if (multicastDelegate != null) { \r
- foreach (Delegate d in multicastDelegate.GetInvocationList()) {\r
- string dn = d.Method.Name;\r
- if (!dn.StartsWith ("dynHandle_"))\r
- continue;\r
- int did = int.Parse (dn.Substring (10));\r
- if (this.DynamicMethodIds.Contains (did))\r
- evtInfo.RemoveEventHandler (ds, d);\r
- }\r
- }\r
- }\r
- }\r
- Interface.Unreference (this);\r
+// object ds = this.DataSource;\r
+// if (ds != null) {\r
+// Type dataSourceType = ds.GetType ();\r
+// EventInfo evtInfo = dataSourceType.GetEvent ("ValueChanged");\r
+// if (evtInfo != null) {\r
+// FieldInfo evtFi = CompilerServices.GetEventHandlerField (dataSourceType, "ValueChanged");\r
+// MulticastDelegate multicastDelegate = evtFi.GetValue (ds) as MulticastDelegate;\r
+// if (multicastDelegate != null) { \r
+// foreach (Delegate d in multicastDelegate.GetInvocationList()) {\r
+// string dn = d.Method.Name;\r
+// if (!dn.StartsWith ("dynHandle_"))\r
+// continue;\r
+// int did = int.Parse (dn.Substring (10));\r
+// if (this.DynamicMethodIds.Contains (did))\r
+// evtInfo.RemoveEventHandler (ds, d);\r
+// }\r
+// }\r
+// }\r
+// }\r
+// Interface.Unreference (this);\r
\r
}\r
}\r