From 4fb8b3a075bd8232be6fdcbb1f5a3b45265d33b4 Mon Sep 17 00:00:00 2001 From: jpbruyere Date: Sat, 19 Nov 2016 11:16:01 +0100 Subject: [PATCH] basic datasource binding functional without inititial set value during dschanged --- Crow.csproj | 424 ++++++----------------- Tests/BasicTests.cs | 2 +- src/CompilerServices/CompilerServices.cs | 32 +- src/GraphicObjects/GraphicObject.cs | 2 +- src/IML/Context.cs | 24 +- src/IML/MemberAddress.cs | 56 ++- src/IML/NodeAddress.cs | 2 + src/IML/Reader.cs | 251 +------------- src/Instantiator.cs | 275 ++++++++++++++- src/ItemTemplate.cs | 2 +- 10 files changed, 466 insertions(+), 604 deletions(-) diff --git a/Crow.csproj b/Crow.csproj index 147537f4..6e43e8dd 100644 --- a/Crow.csproj +++ b/Crow.csproj @@ -39,321 +39,110 @@ __linux__;MEASURE_TIME - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -500,12 +289,9 @@ - - - + PreserveNewest - diff --git a/Tests/BasicTests.cs b/Tests/BasicTests.cs index cd9e3ba3..bd177c6e 100644 --- a/Tests/BasicTests.cs +++ b/Tests/BasicTests.cs @@ -162,7 +162,7 @@ namespace Tests lock (CrowInterface.UpdateMutex) { (CrowInterface.FindByName ("crowContainer") as Container).SetChild (i.CreateInstance(CrowInterface)); - CurSources = i.GetImlSourcesCode(); + //CurSources = i.GetImlSourcesCode(); } } } diff --git a/src/CompilerServices/CompilerServices.cs b/src/CompilerServices/CompilerServices.cs index d8f113e2..18bc78ea 100644 --- a/src/CompilerServices/CompilerServices.cs +++ b/src/CompilerServices/CompilerServices.cs @@ -15,7 +15,7 @@ namespace Crow static MethodInfo miAddBinding = typeof(GraphicObject).GetMethod ("BindMember"); static FieldInfo miSetCurIface = typeof(GraphicObject).GetField ("currentInterface", BindingFlags.NonPublic | BindingFlags.Instance); - static MethodInfo stringEquals = typeof (string).GetMethod + internal static MethodInfo stringEquals = typeof (string).GetMethod ("Equals", new Type [3] { typeof (string), typeof (string), typeof (StringComparison) }); static MethodInfo miFindByName = typeof (GraphicObject).GetMethod ("FindByName"); @@ -38,21 +38,35 @@ namespace Crow #endregion #region ValueChange Reflexion member info - static EventInfo eiValueChange = typeof (IValueChange).GetEvent ("ValueChanged"); - static MethodInfo miInvokeValueChange = eiValueChange.EventHandlerType.GetMethod ("Invoke"); - static Type [] argsValueChange = { typeof (object), typeof (object), miInvokeValueChange.GetParameters () [1].ParameterType }; - static FieldInfo fiNewValue = typeof (ValueChangeEventArgs).GetField ("NewValue"); - static FieldInfo fiMbName = typeof (ValueChangeEventArgs).GetField ("MemberName"); - static MethodInfo miValueChangeAdd = eiValueChange.GetAddMethod (); + internal static EventInfo eiValueChange = typeof (IValueChange).GetEvent ("ValueChanged"); + internal static MethodInfo miInvokeValueChange = eiValueChange.EventHandlerType.GetMethod ("Invoke"); + internal static Type [] argsValueChange = { typeof (object), typeof (object), miInvokeValueChange.GetParameters () [1].ParameterType }; + internal static FieldInfo fiNewValue = typeof (ValueChangeEventArgs).GetField ("NewValue"); + internal static FieldInfo fiMbName = typeof (ValueChangeEventArgs).GetField ("MemberName"); + internal static MethodInfo miValueChangeAdd = eiValueChange.GetAddMethod (); + + internal static EventInfo eiDSChange = typeof (GraphicObject).GetEvent ("DataSourceChanged"); + internal static MethodInfo miInvokeDSChange = eiDSChange.EventHandlerType.GetMethod ("Invoke"); + internal static Type [] argsDSChange = {typeof (object), typeof (object), miInvokeDSChange.GetParameters () [1].ParameterType }; + internal static FieldInfo fiDSCNewDS = typeof (DataSourceChangeEventArgs).GetField ("NewDataSource"); + + internal static MethodInfo miCreateBoundDelegate = typeof(DynamicMethod). + GetMethod("CreateDelegate", new Type[] { typeof(Type), typeof(object)}); + internal static MethodInfo miObjToString = typeof(object).GetMethod("ToString"); + + + internal static Type ehTypeDSChange = eiDSChange.EventHandlerType; + internal static FieldInfo fi_ehTypeDSChange = typeof(CompilerServices).GetField("ehTypeDSChange", BindingFlags.Static | BindingFlags.NonPublic); + #endregion /// - /// Loc0 is the current graphic object and arg1 of loader is the current interface + /// Loc0 is the current graphic object and arg2 of loader is the current interface /// /// Il. public static void emitSetCurInterface(ILGenerator il){ il.Emit (OpCodes.Ldloc_0); - il.Emit (OpCodes.Ldarg_1); + il.Emit (OpCodes.Ldarg_2); il.Emit (OpCodes.Stfld, miSetCurIface); } public static void emitBindingCreation(ILGenerator il, string memberName, string expression){ diff --git a/src/GraphicObjects/GraphicObject.cs b/src/GraphicObjects/GraphicObject.cs index 5cc2ae78..f9b58e00 100644 --- a/src/GraphicObjects/GraphicObject.cs +++ b/src/GraphicObjects/GraphicObject.cs @@ -559,7 +559,7 @@ namespace Crow internal bool localDataSourceIsNull { get { return dataSource == null; } } public virtual void OnDataSourceChanged(object sender, DataSourceChangeEventArgs e){ - DataSourceChanged.Raise (sender, e); + DataSourceChanged.Raise (this, e); //#if DEBUG_BINDING Debug.WriteLine("New DataSource for => {0} \n\t{1}=>{2}", this.ToString(),e.OldDataSource,e.NewDataSource); //#endif diff --git a/src/IML/Context.cs b/src/IML/Context.cs index e5bad2d4..44a9018b 100644 --- a/src/IML/Context.cs +++ b/src/IML/Context.cs @@ -26,6 +26,11 @@ using System.Xml; namespace Crow.IML { + public class DataSourceBinding { + public bool TwoWay; + public MemberAddress Source; + public string DataSourceMember; + } /// /// Context while parsing IML /// @@ -42,23 +47,34 @@ namespace Crow.IML public Dictionary Names = new Dictionary (); public Dictionary> PropertyBindings = new Dictionary> (); + public Dictionary> Bindings = + new Dictionary>(); + public List DataSourceBindings = new List(); + public Context (Type rootType) { RootType = rootType; dm = new DynamicMethod ("dyn_instantiator", - MethodAttributes.Family | MethodAttributes.FamANDAssem | MethodAttributes.NewSlot, - CallingConventions.Standard, - typeof (void), new Type [] { typeof (object), typeof (Interface) }, RootType, true); + typeof (void), new Type [] { typeof (Instantiator), typeof (object), typeof (Interface) }, true); il = dm.GetILGenerator (256); initILGen (); } + + public NodeAddress CurrentNodeAddress { + get { + NodeAddress tmp = new NodeAddress(nodesStack.ToArray ()); + tmp.Add (CurrentNode); + return tmp; + } + } + void initILGen () { il.DeclareLocal (typeof (GraphicObject)); il.Emit (OpCodes.Nop); //set local GraphicObject to root object passed as 1st argument - il.Emit (OpCodes.Ldarg_0); + il.Emit (OpCodes.Ldarg_1); il.Emit (OpCodes.Stloc_0); CompilerServices.emitSetCurInterface (il); } diff --git a/src/IML/MemberAddress.cs b/src/IML/MemberAddress.cs index 85a346e4..89a293c3 100644 --- a/src/IML/MemberAddress.cs +++ b/src/IML/MemberAddress.cs @@ -19,20 +19,44 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . using System; +using System.Linq; +using System.Reflection; + namespace Crow.IML { /// /// Address member of a node /// public struct MemberAddress - { + { + string memberName; + MemberInfo member; public NodeAddress Address; - public string Name; - public MemberAddress (NodeAddress _address, string _member) +// public string Name { +// get { return memberName; } +// set { memberName = value; } +// } + + public MemberAddress (NodeAddress _address, string _member, bool findMember = true) + { + Address = _address; + memberName = _member; + member = null; + + if (!findMember) + return; + if (!tryFindMember ()) + throw new Exception ("Member Not Found: " + memberName); + } + public MemberAddress (NodeAddress _address, MemberInfo _member) { Address = _address; - Name = _member; + member = _member; + memberName = ""; + + if (member != null) + memberName = member.Name; } #region Equality Compare @@ -42,16 +66,36 @@ namespace Crow.IML } public override int GetHashCode () { - return Address.GetHashCode () ^ Name.GetHashCode (); + return Address.GetHashCode () ^ member.GetHashCode (); } public static bool operator == (MemberAddress x, MemberAddress y) { - return x.Address == y.Address && x.Name == y.Name; + return x.Address == y.Address && x.memberName == y.memberName; } public static bool operator != (MemberAddress x, MemberAddress y) { return !(x == y); } #endregion + + bool tryFindMember () + { + if (member != null) + throw new Exception ("member already found"); + if (Address == null) + return false; + Type t = Address.LastOrDefault ().CrowType; + member = t.GetMember (memberName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance).FirstOrDefault (); + + #region search for extensions methods if member not found in type + if (member == null && !string.IsNullOrEmpty (memberName)) { + Assembly a = Assembly.GetExecutingAssembly (); + string mn = memberName; + member = CompilerServices.GetExtensionMethods (a, t).Where (em => em.Name == mn).FirstOrDefault (); + } + #endregion + + return member != null; + } } } diff --git a/src/IML/NodeAddress.cs b/src/IML/NodeAddress.cs index 95341ac7..12ecec38 100644 --- a/src/IML/NodeAddress.cs +++ b/src/IML/NodeAddress.cs @@ -26,6 +26,8 @@ namespace Crow.IML { public class NodeAddress : List { + public NodeAddress (Node[] nodes) : base(nodes) { + } public override bool Equals (object obj) { return obj is NodeAddress && this == obj as NodeAddress; diff --git a/src/IML/Reader.cs b/src/IML/Reader.cs index e4dbc3cb..7a0c8405 100644 --- a/src/IML/Reader.cs +++ b/src/IML/Reader.cs @@ -26,7 +26,7 @@ using System.Reflection.Emit; using System.Linq; using System.Collections.Generic; -namespace Crow.IML +namespace Crow.IML2 { public class Reader : XmlTextReader { @@ -128,187 +128,14 @@ namespace Crow.IML } #endregion - void createInstantiator(){ - context = new Context(); - RootType = findRootType(); - InitEmitter(); - emitLoader(RootType); - Read();//close tag - } void createBindingDelegates(){ // foreach (Dictionary pb in IMLCtx.PropertyBindings) { // // } } - /// - /// Inits il generator, RootType must have been read first - /// - void InitEmitter(){ - - dm = new DynamicMethod("dyn_instantiator", - MethodAttributes.Family | MethodAttributes.FamANDAssem | MethodAttributes.NewSlot, - CallingConventions.Standard, - typeof(void),new Type[] {typeof(object), typeof(Interface)}, RootType, true); - - context.il = dm.GetILGenerator(256); - il.DeclareLocal(typeof(GraphicObject)); - il.Emit(OpCodes.Nop); - //set local GraphicObject to root object passed as 1st argument - il.Emit (OpCodes.Ldarg_0); - il.Emit (OpCodes.Stloc_0); - CompilerServices.emitSetCurInterface (il); - } - void emitLoader(Type crowType){ - Node expectedNode = new Node (crowType); - - string tmpXml = ReadOuterXml (); - - il.Emit (OpCodes.Ldloc_0);//save current go onto the stack if child has to be added - - #region Template and ItemTemplates loading - if (expectedNode.IsTemplate) { - //if its a template, first read template elements - using (Reader reader = new Reader (context, tmpXml)) { - List itemTemplateIds = new List (); - bool inlineTemplate = false; - - string templatePath = reader.GetAttribute ("Template"); - - reader.Read (); - int depth = reader.Depth + 1; - while (reader.Read ()) { - if (!reader.IsStartElement () || reader.Depth > depth) - continue; - if (reader.Name == "Template") { - inlineTemplate = true; - reader.Read (); - context.nodesStack.Push (expectedNode); - readChildren (reader); - context.nodesStack.Pop(); - } else if (reader.Name == "ItemTemplate") { - string dataType = "default", datas = "", path = ""; - while (reader.MoveToNextAttribute ()) { - if (reader.Name == "DataType") - dataType = reader.Value; - else if (reader.Name == "Data") - datas = reader.Value; - else if (reader.Name == "Path") - path = reader.Value; - } - reader.MoveToElement (); - - string itemTmpID; - - if (string.IsNullOrEmpty (path)) { - using (Reader iTmp = new Reader (null, reader.ReadInnerXml ())) { - itemTmpID = Guid.NewGuid ().ToString (); - Interface.Instantiators [itemTmpID] = - new ItemTemplate (iTmp.RootType, iTmp.GetLoader (), dataType, datas); - } - }else{ - if (!reader.IsEmptyElement) - throw new Exception ("ItemTemplate with Path attribute may not include sub nodes"); - itemTmpID = path; - Interface.Instantiators [itemTmpID] = - new ItemTemplate (itemTmpID, dataType, datas); - } - itemTemplateIds.Add (new string[] { dataType, itemTmpID, datas }); - } - } - - if (!inlineTemplate) {//load from path or default template - reader.il.Emit (OpCodes.Ldloc_0);//Load current templatedControl ref - if (string.IsNullOrEmpty (templatePath)) { - reader.il.Emit (OpCodes.Ldnull);//default template loading - }else{ - reader.il.Emit (OpCodes.Ldarg_1);//load currentInterface - reader.il.Emit (OpCodes.Ldstr, templatePath); //Load template path string - reader.il.Emit (OpCodes.Callvirt,//call Interface.Load(path) - CompilerServices.miIFaceLoad); - } - reader.il.Emit (OpCodes.Callvirt, CompilerServices.miLoadTmp);//load template - } - //copy item templates (review this) - foreach (string[] iTempId in itemTemplateIds) { - reader.il.Emit (OpCodes.Ldloc_0);//load TempControl ref - reader.il.Emit (OpCodes.Ldfld, CompilerServices.fldItemTemplates);//load ItemTemplates dic field - reader.il.Emit (OpCodes.Ldstr, iTempId[0]);//load key - reader.il.Emit (OpCodes.Ldstr, iTempId[1]);//load value - reader.il.Emit (OpCodes.Callvirt, CompilerServices.miGetITemp); - reader.il.Emit (OpCodes.Callvirt, CompilerServices.miAddITemp); - - if (!string.IsNullOrEmpty (iTempId [2])) { - //expand delegate creation - reader.il.Emit (OpCodes.Ldloc_0);//load TempControl ref - reader.il.Emit (OpCodes.Ldfld, CompilerServices.fldItemTemplates); - reader.il.Emit (OpCodes.Ldstr, iTempId [0]);//load key - reader.il.Emit (OpCodes.Callvirt, CompilerServices.miGetITempFromDic); - reader.il.Emit (OpCodes.Ldloc_0);//load root of treeView - reader.il.Emit (OpCodes.Callvirt, CompilerServices.miCreateExpDel); - } - } - } - expectedNode.Index++; - } - #endregion - - using (Reader reader = new Reader (context, tmpXml)){ - reader.Read (); - - #region Styling and default values loading - if (reader.HasAttributes) { - string style = reader.GetAttribute ("Style"); - if (!string.IsNullOrEmpty (style)) - CompilerServices.EmitSetValue (reader.il, CompilerServices.piStyle, style); - } - reader.il.Emit (OpCodes.Ldloc_0); - reader.il.Emit (OpCodes.Callvirt, CompilerServices.miLoadDefaultVals); - #endregion - - #region Attributes reading - if (reader.HasAttributes) { - - while (reader.MoveToNextAttribute ()) { - if (reader.Name == "Style") - continue; - - MemberInfo mi = crowType.GetMember (reader.Name).FirstOrDefault(); - if (mi == null) - throw new Exception ("Member '" + reader.Name + "' not found in " + crowType.Name); - if (mi.MemberType == MemberTypes.Event) { - CompilerServices.emitBindingCreation (reader.il, reader.Name, reader.Value); - continue; - } - PropertyInfo pi = mi as PropertyInfo; - if (pi == null) - throw new Exception ("Member '" + reader.Name + "' not found in " + crowType.Name); - - if (pi.Name == "Name") - context.Names.Add(reader.Value, Node.AddressToString(context.nodesStack.ToArray())); - - if (reader.Value.StartsWith ("{", StringComparison.OrdinalIgnoreCase)) { - readPropertyBinding(reader.Name, reader.Value.Substring (1, reader.Value.Length - 2)); - //CompilerServices.emitBindingCreation (reader.il, reader.Name, reader.Value.Substring (1, reader.Value.Length - 2)); - }else - CompilerServices.EmitSetValue (reader.il, pi, reader.Value); - } - reader.MoveToElement (); - } - #endregion - - if (reader.IsEmptyElement) { - reader.il.Emit (OpCodes.Pop);//pop saved ref to current object - return; - } - context.nodesStack.Push (expectedNode); - readChildren (reader); - context.nodesStack.Pop (); - } - il.Emit (OpCodes.Pop);//pop saved ref to current object - } void registerPropertyBinding(string origNode, string origProp, MemberAddress ma){ if (!context.PropertyBindings.ContainsKey(origNode)) context.PropertyBindings [origNode] = new Dictionary (); @@ -390,83 +217,7 @@ namespace Crow.IML registerPropertyBinding (nodeId, targetName, new MemberAddress (context.nodesStack.ToArray (), srcProperty)); } - /// - /// Parse child node an generate corresponding msil - /// - void readChildren(Reader reader){ - bool endTagReached = false; - while (reader.Read()){ - switch (reader.NodeType) { - case XmlNodeType.EndElement: - endTagReached = true; - break; - case XmlNodeType.Element: - //Templates - if (reader.Name == "Template" || - reader.Name == "ItemTemplate") { - reader.Skip (); - continue; - } - - - //push current instance on stack for parenting - //loc_0 will be used for child - reader.il.Emit (OpCodes.Ldloc_0); - - Type t = Type.GetType ("Crow." + reader.Name); - if (t == null) { - Assembly a = Assembly.GetEntryAssembly (); - foreach (Type expT in a.GetExportedTypes ()) { - if (expT.Name == reader.Name) - t = expT; - } - } - if (t == null) - throw new Exception (reader.Name + " type not found"); - - reader.il.Emit (OpCodes.Newobj, t.GetConstructors () [0]);//TODO:search parameterless ctor - reader.il.Emit (OpCodes.Stloc_0);//child is now loc_0 - CompilerServices.emitSetCurInterface (il); - - reader.emitLoader (t); - - reader.il.Emit (OpCodes.Ldloc_0);//load child on stack for parenting - reader.il.Emit (OpCodes.Callvirt, miAddChild [(int)context.nodesStack.Peek ().Type -1]); - reader.il.Emit (OpCodes.Stloc_0); //reset local to current go - reader.il.Emit (OpCodes.Ldloc_0);//save current go onto the stack if child has to be added - context.nodesStack.Peek ().Index++; - - break; - } - if (endTagReached) - break; - } - } - /// - /// read first node to set GraphicObject class for loading - /// and let reader position on that node - /// - Type findRootType () - { - string root = "Object"; - while (Read()) { - if (NodeType == XmlNodeType.Element) { - root = this.Name; - break; - } - } - - Type t = Type.GetType ("Crow." + root); - if (t == null) { - Assembly a = Assembly.GetEntryAssembly (); - foreach (Type expT in a.GetExportedTypes ()) { - if (expT.Name == root) - t = expT; - } - } - return t; - } } } diff --git a/src/Instantiator.cs b/src/Instantiator.cs index 0c8391cd..0bd33278 100644 --- a/src/Instantiator.cs +++ b/src/Instantiator.cs @@ -28,13 +28,14 @@ using System.Reflection.Emit; using System.Reflection; using System.Collections.Generic; using System.Linq; +using Crow.IML; -namespace Crow.IML +namespace Crow { public delegate void InstanciatorInvoker(object instance, Interface iface); /// - /// Instantiators + /// Instantiator /// public class Instantiator { @@ -76,6 +77,16 @@ namespace Crow.IML } #endregion + public GraphicObject CreateInstance(Interface iface){ + GraphicObject tmp = (GraphicObject)Activator.CreateInstance(RootType); + loader (tmp, iface); + return tmp; + } + + List dsValueChangedDynMeths = new List(); + List dataSourceChangedDelegates = new List(); + + #region IML parsing /// /// Parses IML and build a dynamic method that will be used to instanciate one or multiple occurence of the IML file or fragment /// @@ -85,7 +96,11 @@ namespace Crow.IML ctx.CurrentNode = new Node (ctx.RootType); emitLoader (reader, ctx); + ctx.il.Emit(OpCodes.Ret); + reader.Read ();//close tag + RootType = ctx.RootType; + loader = (InstanciatorInvoker)ctx.dm.CreateDelegate (typeof (InstanciatorInvoker), this); } /// /// read first node to set GraphicObject class for loading @@ -126,7 +141,7 @@ namespace Crow.IML } void emitTemplateLoad (Context ctx, string tmpXml) { //if its a template, first read template elements - using (XmlTextReader reader = new XmlTextReader (tmpXml)) { + using (XmlTextReader reader = new XmlTextReader (tmpXml, XmlNodeType.Element, null)) { List itemTemplateIds = new List (); bool inlineTemplate = false; @@ -178,7 +193,7 @@ namespace Crow.IML if (string.IsNullOrEmpty (templatePath)) { ctx.il.Emit (OpCodes.Ldnull);//default template loading } else { - ctx.il.Emit (OpCodes.Ldarg_1);//load currentInterface + ctx.il.Emit (OpCodes.Ldarg_2);//load currentInterface ctx.il.Emit (OpCodes.Ldstr, templatePath); //Load template path string ctx.il.Emit (OpCodes.Callvirt,//call Interface.Load(path) CompilerServices.miIFaceLoad); @@ -209,7 +224,7 @@ namespace Crow.IML } void emitGOLoad (Context ctx, string tmpXml) { - using (XmlTextReader reader = new XmlTextReader (tmpXml)) { + using (XmlTextReader reader = new XmlTextReader (tmpXml, XmlNodeType.Element, null)) { reader.Read (); #region Styling and default values loading @@ -240,11 +255,13 @@ namespace Crow.IML if (pi == null) throw new Exception ("Member '" + reader.Name + "' not found in " + ctx.CurrentNode.CrowType.Name); - if (pi.Name == "Name") - ctx.Names.Add (reader.Value, Node.AddressToString (ctx.nodesStack.ToArray ())); + //if (pi.Name == "Name") + // ctx.Names.Add (reader.Value, Node.AddressToString (ctx.nodesStack.ToArray ())); if (reader.Value.StartsWith ("{", StringComparison.OrdinalIgnoreCase)) { - readPropertyBinding (reader.Name, reader.Value.Substring (1, reader.Value.Length - 2)); + + + readPropertyBinding (ctx, reader.Name, reader.Value.Substring (1, reader.Value.Length - 2)); //CompilerServices.emitBindingCreation (reader.il, reader.Name, reader.Value.Substring (1, reader.Value.Length - 2)); } else @@ -256,9 +273,10 @@ namespace Crow.IML #endregion if (reader.IsEmptyElement) { - ctx.il.Emit (OpCodes.Pop);//pop saved ref to current object + //ctx.il.Emit (OpCodes.Pop);//pop saved ref to current object return; } + ctx.nodesStack.Push (ctx.CurrentNode); readChildren (reader, ctx); ctx.nodesStack.Pop (); @@ -318,13 +336,244 @@ namespace Crow.IML break; } } + #endregion + /// + /// Tries the find target. + /// + /// The member address in the graphic tree, null if on dataSource, + /// Expression. + void readPropertyBinding (Context ctx, string sourceMember, string expression) + { + MemberAddress targetMember; + NodeAddress target; - public GraphicObject CreateInstance(Interface iface){ - GraphicObject tmp = (GraphicObject)Activator.CreateInstance(RootType); - loader (tmp, iface); - return tmp; + string memberName = null; + bool twoWay = false; + + //if binding exp = '{}' => binding is done on datasource + if (string.IsNullOrEmpty (expression)) + return; + + if (expression.StartsWith ("²")) { + expression = expression.Substring (1); + twoWay = true; + } + + string [] bindingExp = expression.Split ('/'); + + if (bindingExp.Length == 1) { + //datasource binding + processDataSourceBinding(ctx, sourceMember, bindingExp [0]); + + } +// else { +// int ptr = 0; +// +// //if exp start with '/' => Graphic tree parsing start at source +// if (string.IsNullOrEmpty (bindingExp [0])) +// ptr++; +// else if (bindingExp[0] == "."){ //search template root +// do { +// target = new NodeAddress(ctx. +// if (tmpTarget == null) +// return false; +// if (tmpTarget is Interface) +// throw new Exception ("Not in Templated Control"); +// } while (!(tmpTarget is TemplatedControl)); +// ptr++; +// } +// while (ptr < bindingExp.Length - 1) { +// if (tmpTarget == null) { +// #if DEBUG_BINDING +// Debug.WriteLine ("\tTarget not found => " + this.ToString()); +// #endif +// return false; +// } +// if (bindingExp [ptr] == "..") +// tmpTarget = tmpTarget.LogicalParent; +// else if (bindingExp [ptr] == ".") { +// if (ptr > 0) +// throw new Exception ("Syntax error in binding, './' may only appear in first position"); +// tmpTarget = Source.Instance as ILayoutable; +// } else +// tmpTarget = (tmpTarget as GraphicObject).FindByName (bindingExp [ptr]); +// ptr++; +// } +// +// if (tmpTarget == null) { +// #if DEBUG_BINDING +// Debug.WriteLine ("\tBinding Target not found => " + this.ToString()); +// #endif +// return false; +// } +// +// string [] bindTrg = bindingExp [ptr].Split ('.'); +// +// if (bindTrg.Length == 1) +// memberName = bindTrg [0]; +// else if (bindTrg.Length == 2) { +// tmpTarget = (tmpTarget as GraphicObject).FindByName (bindTrg [0]); +// memberName = bindTrg [1]; +// } else +// throw new Exception ("Syntax error in binding, expected 'go dot member'"); +// +// if (tmpTarget == null) { +// #if DEBUG_BINDING +// Debug.WriteLine ("\tBinding Target not found => " + this.ToString()); +// #endif +// return false; +// } +// +// Target = new MemberReference (tmpTarget); +// } +// +// if (Target.TryFindMember (memberName)) { +// if (TwoWayBinding) { +// // IBindable source = Target.Instance as IBindable; +// // if (source == null) +// // throw new Exception (Source.Instance + " does not implement IBindable for 2 way bindings"); +// // source.Bindings.Add (new Binding (Target, Source)); +// } +// } + #if DEBUG_BINDING + else + Debug.WriteLine ("Property less binding: " + Target + expression); + #endif + } + + void processDataSourceBinding(Context ctx, string sourceMember, string dataSourceMember){ + #region create valuechanged method + DynamicMethod dm = new DynamicMethod ("dyn_valueChanged", + typeof (void), + CompilerServices.argsValueChange, true); + + ILGenerator il = dm.GetILGenerator (256); + + System.Reflection.Emit.Label endMethod = il.DefineLabel (); + + il.Emit (OpCodes.Nop); + + //load value changed member name onto the stack + il.Emit (OpCodes.Ldarg_2); + il.Emit (OpCodes.Ldfld, CompilerServices.fiMbName); + + //test if it's the expected one + il.Emit (OpCodes.Ldstr, dataSourceMember); + il.Emit (OpCodes.Ldc_I4_4);//StringComparison.Ordinal + il.Emit (OpCodes.Callvirt, CompilerServices.stringEquals); + il.Emit (OpCodes.Brfalse, endMethod); + //set destination member with valueChanged new value + //load destination ref + il.Emit (OpCodes.Ldarg_0); + //load new value onto the stack + il.Emit (OpCodes.Ldarg_2); + il.Emit (OpCodes.Ldfld, CompilerServices.fiNewValue); + + //by default, source value type is deducted from target member type to allow + //memberless binding, if targetMember exists, it will be used to determine target + //value type for conversion + PropertyInfo piSource = ctx.CurrentNode.CrowType.GetProperty(sourceMember); + Type sourceValueType = piSource.PropertyType; + + if (sourceValueType == typeof (string)) { + il.Emit (OpCodes.Callvirt, CompilerServices.miObjToString); + } else if (!sourceValueType.IsValueType) + il.Emit (OpCodes.Castclass, sourceValueType); + else if (sourceValueType != sourceValueType) { + il.Emit (OpCodes.Callvirt, CompilerServices.GetConvertMethod (sourceValueType)); + } else + il.Emit (OpCodes.Unbox_Any, sourceValueType); + + il.Emit (OpCodes.Callvirt, piSource.GetSetMethod ()); + //il.Emit (OpCodes.Pop); + //il.Emit (OpCodes.Pop); + + il.MarkLabel (endMethod); + + il.Emit (OpCodes.Ret); + + //vc dyn meth is stored in a cached list, it will be bound to datasource only + //when datasource of source graphic object changed + int dmVC = dsValueChangedDynMeths.Count; + //Delegate tmp = dm.CreateDelegate(typeof(EventHandler), this); + dsValueChangedDynMeths.Add (dm); + #endregion + + #region emit dataSourceChanged event handler + //now we create the datasource changed method that will init the destination member with + //the actual value of the origin member of the datasource and then will bind the value changed + //dyn methode. + //dm is bound to the instanciator instance to have access to cached dyn meth and delegates + dm = new DynamicMethod ("dyn_dschanged", + typeof (void), + CompilerServices.argsDSChange, true); + + il = dm.GetILGenerator (256); + //il.DeclareLocal (typeof(Object[])); + + il.Emit (OpCodes.Nop); + +// //load new datasource onto the stack for handler addition at the end + il.Emit (OpCodes.Ldarg_2); + il.Emit (OpCodes.Ldfld, CompilerServices.fiDSCNewDS); + + #region Load cached delegate + il.Emit(OpCodes.Ldarg_0);//load ref to this instanciator onto the stack + il.Emit(OpCodes.Ldfld, typeof(Instantiator).GetField("dsValueChangedDynMeths", BindingFlags.Instance | BindingFlags.NonPublic)); + il.Emit(OpCodes.Ldc_I4, dmVC); + il.Emit(OpCodes.Callvirt, typeof(List).GetMethod("get_Item", new Type[] { typeof(Int32) })); + #endregion + + //load ds changed eventhandlertype + il.Emit(OpCodes.Ldtoken, typeof(EventHandler)); + il.Emit(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle", new + Type[1]{typeof(RuntimeTypeHandle)})); + //typeof (GraphicObject).GetEvent ("DataSourceChanged").EventHandlerType; + //il.Emit(OpCodes.Ldfld, typeof(CompilerServices).GetField("ehTypeDSChange", BindingFlags.Static | BindingFlags.NonPublic);); + + //create object[] for delegate creation parameters +// il.Emit (OpCodes.Ldc_I4_1); +// il.Emit (OpCodes.Newarr, typeof(object)); +// il.Emit (OpCodes.Stloc_0);//save array ref +// il.Emit (OpCodes.Ldloc_0);//load array ref onto the stack +// il.Emit (OpCodes.Ldc_I4_0);//0 is the index of the dm in the array + + //load datasource change source + il.Emit (OpCodes.Ldarg_1); + //il.Emit (OpCodes.Ldfld, CompilerServices.fiDSCNewDS); + + //create bound delegate + il.Emit (OpCodes.Call, CompilerServices.miCreateBoundDelegate); + //set delegete as index 0 in the object array +// il.Emit(OpCodes.Ldelem_I4); + + //add new delegate to datasource valuechanged event + il.Emit(OpCodes.Callvirt, typeof(IValueChange).GetEvent("ValueChanged").AddMethod);//call add event //il.Emit(OpCodes.Pop); + //il.Emit(OpCodes.Pop); + //il.Emit(OpCodes.Pop); + //il.Emit(OpCodes.Pop); + il.Emit (OpCodes.Ret); + + + #endregion + + int delDSIndex = dataSourceChangedDelegates.Count; + Delegate del = dm.CreateDelegate (CompilerServices.ehTypeDSChange, this); + dataSourceChangedDelegates.Add(del); + + #region Emit datasourcechanged handler binding in the loader context + ctx.il.Emit(OpCodes.Ldloc_0);//load ref to current graphic object + ctx.il.Emit(OpCodes.Ldarg_0);//load ref to this instanciator onto the stack + ctx.il.Emit(OpCodes.Ldfld, typeof(Instantiator).GetField("dataSourceChangedDelegates", BindingFlags.Instance | BindingFlags.NonPublic)); + ctx.il.Emit(OpCodes.Ldc_I4, delDSIndex);//load delegate index + ctx.il.Emit(OpCodes.Callvirt, typeof(List).GetMethod("get_Item", new Type[] { typeof(Int32) })); + + ctx.il.Emit(OpCodes.Callvirt, typeof(GraphicObject).GetEvent("DataSourceChanged").AddMethod);//call add event + #endregion + //miValueChangeAdd.Invoke (grouped [0].Target.Instance, new object [] { del }); } + //public string GetImlSourcesCode(){ // try { // using (StreamReader sr = new StreamReader (imlPath)) diff --git a/src/ItemTemplate.cs b/src/ItemTemplate.cs index 4fb85638..9c48a2db 100644 --- a/src/ItemTemplate.cs +++ b/src/ItemTemplate.cs @@ -45,7 +45,7 @@ namespace Crow } public ItemTemplate (Stream ImlFragment,string _dataType, string _fetchDataMethod) :base(ImlFragment) - {+- + { strDataType = _dataType; fetchMethodName = _fetchDataMethod; } -- 2.47.3