From ce8c0dabfc960be91c36434ccd3ef7cdfe99fd74 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Jean-Philippe=20Bruy=C3=A8re?= Date: Sat, 17 Feb 2018 12:13:48 +0100 Subject: [PATCH] first working test with hierarchical TemplatedGroup with only one root Level --- Templates/DirectoryView.template | 3 +- src/CompilerServices/CompilerServices.cs | 3 + src/GraphicObjects/TemplatedGroup.cs | 144 ++++++++++++----------- src/Instantiator.cs | 14 ++- src/ItemTemplate.cs | 71 +++++++---- 5 files changed, 136 insertions(+), 99 deletions(-) diff --git a/Templates/DirectoryView.template b/Templates/DirectoryView.template index 42e9020f..826dd3f8 100755 --- a/Templates/DirectoryView.template +++ b/Templates/DirectoryView.template @@ -37,8 +37,7 @@ - + diff --git a/src/CompilerServices/CompilerServices.cs b/src/CompilerServices/CompilerServices.cs index 16d28490..dbd12d41 100644 --- a/src/CompilerServices/CompilerServices.cs +++ b/src/CompilerServices/CompilerServices.cs @@ -92,6 +92,9 @@ namespace Crow.IML internal static MethodInfo miAddITemp = typeof(Dictionary).GetMethod ("set_Item", new Type[] { typeof(string), typeof(ItemTemplate) }); internal static MethodInfo miGetITempFromDic = typeof(Dictionary).GetMethod ("get_Item", new Type[] { typeof(string) }); internal static FieldInfo fldItemTemplates = typeof(TemplatedGroup).GetField("ItemTemplates"); + internal static MethodInfo miLoadPage = typeof(TemplatedGroup).GetMethod ("loadPage", BindingFlags.Instance | BindingFlags.NonPublic| BindingFlags.Public); + internal static MethodInfo miIsAlreadyExpanded = typeof(TemplatedGroup).GetMethod("emitHelperIsAlreadyExpanded", BindingFlags.Instance | BindingFlags.NonPublic); + internal static MethodInfo miCreateExpDel = typeof(ItemTemplate).GetMethod ("CreateExpandDelegate"); internal static FieldInfo fiFetchMethodName = typeof(ItemTemplate).GetField("fetchMethodName", BindingFlags.Instance | BindingFlags.NonPublic); #region tree handling methods diff --git a/src/GraphicObjects/TemplatedGroup.cs b/src/GraphicObjects/TemplatedGroup.cs index ef099f93..c72bfbcb 100644 --- a/src/GraphicObjects/TemplatedGroup.cs +++ b/src/GraphicObjects/TemplatedGroup.cs @@ -34,6 +34,7 @@ using System.Collections; using System.Threading; using System.Linq; using Crow.IML; +using System.Diagnostics; namespace Crow { @@ -45,7 +46,7 @@ namespace Crow #endregion protected Group items; - string _itemTemplate, _dataTest; + string _itemTemplate, dataTest; #region events public event EventHandler SelectedItemChanged; @@ -66,6 +67,11 @@ namespace Crow //but then i should test if null in msil gen public Dictionary ItemTemplates = new Dictionary(); + /// + /// Keep track of expanded subnodes and closed time to unload + /// + //Dictionary nodes = new Dictionary(); + internal List nodes = new List(); /// /// Item templates file path, on disk or embedded. /// @@ -106,14 +112,14 @@ namespace Crow /// The data property test. [XmlAttributeAttribute][DefaultValue("TypeOf")] public string DataTest { - get { return _dataTest; } + get { return dataTest; } set { - if (value == _dataTest) + if (value == dataTest) return; - _dataTest = value; + dataTest = value; - NotifyValueChanged("DataTest", _dataTest); + NotifyValueChanged("DataTest", dataTest); } } #endregion @@ -170,7 +176,7 @@ namespace Crow if (value == data) return; - cancelLoadingThread (); + //cancelLoadingThread (); if (data is IObservableList) { IObservableList ol = data as IObservableList; @@ -188,15 +194,17 @@ namespace Crow NotifyValueChanged ("Data", data); - lock (CurrentInterface.LayoutMutex) + //lock (CurrentInterface.LayoutMutex) ClearItems (); if (data == null) return; - loadingThread = new CrowThread (this, loading); - loadingThread.Finished += (object sender, EventArgs e) => (sender as TemplatedGroup).Loaded.Raise (sender, e); - loadingThread.Start (); +// loadingThread = new CrowThread (this, loading); +// loadingThread.Finished += (object sender, EventArgs e) => (sender as TemplatedGroup).Loaded.Raise (sender, e); +// loadingThread.Start (); + + loadPage (data, items, dataTest); NotifyValueChanged ("SelectedIndex", _selectedIndex); NotifyValueChanged ("SelectedItem", SelectedItem); @@ -223,7 +231,7 @@ namespace Crow // int i = e.Index % itemPerPage; // (items.Children [p] as Group).InsertChild (i, e.Element); } else - loadItem (e.Index, items); + loadItem (e.Element, items, dataTest); } [XmlAttributeAttribute][DefaultValue("SteelBlue")] @@ -319,69 +327,62 @@ namespace Crow /// /// Items loading thread /// - void loading(){ - //if (!ItemTemplates.ContainsKey ("default")) - // ItemTemplates ["default"] = Interface.GetItemTemplate (ItemTemplate); - - for (int i = 1; i <= (data.Count / itemPerPage) + 1; i++) { - if ((bool)loadingThread?.cancelRequested) { - this.Dispose (); - return; - } - loadPage (i); - Thread.Sleep (1); - } - } +// void loading(){ +// //if (!ItemTemplates.ContainsKey ("default")) +// // ItemTemplates ["default"] = Interface.GetItemTemplate (ItemTemplate); +// +// for (int i = 1; i <= (data.Count / itemPerPage) + 1; i++) { +// if ((bool)loadingThread?.cancelRequested) { +// this.Dispose (); +// return; +// } +// loadPage (i); +// Thread.Sleep (1); +// } +// } void cancelLoadingThread(){ if (loadingThread != null) loadingThread.Cancel (); } - void loadPage(int pageNum) + void loadPage(IList _data, Group page, string _dataTest) { #if DEBUG_LOAD Stopwatch loadingTime = new Stopwatch (); loadingTime.Start (); #endif - Group page; - if (typeof(TabView).IsAssignableFrom (items.GetType ())|| - typeof(Menu).IsAssignableFrom (this.GetType())|| - typeof(Wrapper).IsAssignableFrom (items.GetType ())) { - page = items; - itemPerPage = int.MaxValue; - } else if (typeof(GenericStack).IsAssignableFrom (items.GetType ())) { - GenericStack gs = new GenericStack (items.CurrentInterface); - gs.Orientation = (items as GenericStack).Orientation; - gs.Width = items.Width; - gs.Height = items.Height; - gs.VerticalAlignment = items.VerticalAlignment; - gs.HorizontalAlignment = items.HorizontalAlignment; - page = gs; - page.Name = "page" + pageNum; - isPaged = true; - } else { - page = Activator.CreateInstance (items.GetType ()) as Group; - page.CurrentInterface = items.CurrentInterface; - page.Initialize (); - page.Name = "page" + pageNum; - isPaged = true; - } - - for (int i = (pageNum - 1) * itemPerPage; i < pageNum * itemPerPage; i++) { - if (i >= data.Count) - break; - if ((bool)loadingThread?.cancelRequested) { - page.Dispose (); - return; - } - loadItem (i, page); +// if (typeof(TabView).IsAssignableFrom (items.GetType ())|| +// typeof(Menu).IsAssignableFrom (this.GetType())|| +// typeof(Wrapper).IsAssignableFrom (items.GetType ())) { + //page = items; + itemPerPage = int.MaxValue; +// } else if (typeof(GenericStack).IsAssignableFrom (items.GetType ())) { +// GenericStack gs = new GenericStack (items.CurrentInterface); +// gs.Orientation = (items as GenericStack).Orientation; +// gs.Width = items.Width; +// gs.Height = items.Height; +// gs.VerticalAlignment = items.VerticalAlignment; +// gs.HorizontalAlignment = items.HorizontalAlignment; +// page = gs; +// page.Name = "page" + pageNum; +// isPaged = true; +// } else { +// page = Activator.CreateInstance (items.GetType ()) as Group; +// page.CurrentInterface = items.CurrentInterface; +// page.Initialize (); +// page.Name = "page" + pageNum; +// isPaged = true; +// } + + for (int i = 0; i < _data.Count; i++) { + loadItem (_data[i], page, _dataTest); } - if (page == items) - return; - lock (CurrentInterface.LayoutMutex) - items.AddChild (page); +// if (page == items) +// return; +// lock (CurrentInterface.LayoutMutex) +// items.AddChild (page); #if DEBUG_LOAD loadingTime.Stop (); @@ -390,23 +391,23 @@ namespace Crow loadingTime.ElapsedMilliseconds, this.ToString()); #endif } - string getItempKey(Type dataType, object o){ + string getItempKey(Type dataType, object o, string propertyName){ try { - return dataType.GetProperty (_dataTest).GetGetMethod ().Invoke (o, null)?.ToString(); + return dataType.GetProperty (propertyName).GetGetMethod ().Invoke (o, null)?.ToString(); } catch { return dataType.FullName; } } - protected void loadItem(int i, Group page){ - if (data [i] == null)//TODO:surely a threading sync problem + protected void loadItem(object o, Group page, string _dataTest){ + if (o == null)//TODO:surely a threading sync problem return; GraphicObject g = null; ItemTemplate iTemp = null; - Type dataType = data [i].GetType (); + Type dataType = o.GetType (); string itempKey = dataType.FullName; if (_dataTest != "TypeOf") - itempKey = getItempKey (dataType, data [i]); + itempKey = getItempKey (dataType, o, _dataTest); if (ItemTemplates.ContainsKey (itempKey)) iTemp = ItemTemplates [itempKey]; @@ -437,7 +438,7 @@ namespace Crow (g as Expandable).GetIsExpandable = iTemp.HasSubItems; } - g.DataSource = data [i]; + g.DataSource = o; } protected virtual void registerItemClick(GraphicObject g){ g.MouseClick += itemClick; @@ -475,6 +476,13 @@ namespace Crow SelectedIndex = data.IndexOf((sender as GraphicObject).DataSource); } + bool emitHelperIsAlreadyExpanded (GraphicObject go){ + if (nodes.Contains (go)) + return true; + nodes.Add (go); + return false; + } + protected override void Dispose (bool disposing) { if (disposing && loadingThread != null) diff --git a/src/Instantiator.cs b/src/Instantiator.cs index a97f19d2..b01052ca 100644 --- a/src/Instantiator.cs +++ b/src/Instantiator.cs @@ -225,7 +225,7 @@ namespace Crow.IML /// current xml text reader /// file containing the templates if its a dedicated one string[] parseItemTemplateTag (XmlReader reader, string itemTemplatePath = "") { - string dataType = "default", datas = "", path = ""; + string dataType = "default", datas = "", path = "", dataTest = "TypeOf"; while (reader.MoveToNextAttribute ()) { if (reader.Name == "DataType") dataType = reader.Value; @@ -233,6 +233,8 @@ namespace Crow.IML datas = reader.Value; else if (reader.Name == "Path") path = reader.Value; + else if (reader.Name == "DataTest") + dataTest = reader.Value; } reader.MoveToElement (); @@ -241,7 +243,7 @@ namespace Crow.IML if (string.IsNullOrEmpty (path)) { itemTmpID += Guid.NewGuid ().ToString (); Interface.Instantiators [itemTmpID] = - new ItemTemplate (new MemoryStream (Encoding.UTF8.GetBytes (reader.ReadInnerXml ())), dataType, datas); + new ItemTemplate (new MemoryStream (Encoding.UTF8.GetBytes (reader.ReadInnerXml ())), dataTest, dataType, datas); } else { if (!reader.IsEmptyElement) @@ -249,9 +251,9 @@ namespace Crow.IML itemTmpID += path+dataType+datas; if (!Interface.Instantiators.ContainsKey (itemTmpID)) Interface.Instantiators [itemTmpID] = - new ItemTemplate (Interface.GetStreamFromPath (path), dataType, datas); + new ItemTemplate (Interface.GetStreamFromPath (path), dataTest, dataType, datas); } - return new string [] { dataType, itemTmpID, datas }; + return new string [] { dataType, itemTmpID, datas, dataTest }; } /// /// process template and item template definition prior to @@ -312,7 +314,7 @@ namespace Crow.IML //the file contains a single template to use as default Interface.Instantiators [itemTemplatePath] = new ItemTemplate (itr); - itemTemplateIds.Add (new string [] { "default", itemTemplatePath, "" }); + itemTemplateIds.Add (new string [] { "default", itemTemplatePath, "", "TypeOf" }); break;//we should be at the end of the file } itemTemplateIds.Add (parseItemTemplateTag (itr, itemTemplatePath)); @@ -327,7 +329,7 @@ namespace Crow.IML return; //add the default item template if no default is defined if (!itemTemplateIds.Any(ids=>ids[0] == "default")) - itemTemplateIds.Add (new string [] { "default", "#Crow.DefaultItem.template", "" }); + itemTemplateIds.Add (new string [] { "default", "#Crow.DefaultItem.template", "", "TypeOf"}); //copy item templates (review this) foreach (string [] iTempId in itemTemplateIds) { ctx.il.Emit (OpCodes.Ldloc_0);//load TempControl ref diff --git a/src/ItemTemplate.cs b/src/ItemTemplate.cs index 52bd5207..5ede1d94 100644 --- a/src/ItemTemplate.cs +++ b/src/ItemTemplate.cs @@ -52,6 +52,7 @@ namespace Crow public BooleanTestOnInstance HasSubItems; string strDataType; string fetchMethodName; + string dataTest; #region CTOR /// @@ -60,10 +61,11 @@ namespace Crow /// IML file to parse /// type this item will be choosen for, or member of the data item /// for hierarchical data, method to call for children fetching - public ItemTemplate(string path, string _dataType = null, string _fetchDataMethod = null) + public ItemTemplate(string path, string _dataTest = "TypeOf", string _dataType = null, string _fetchDataMethod = null) : base(path) { strDataType = _dataType; fetchMethodName = _fetchDataMethod; + dataTest = _dataTest; } /// @@ -72,11 +74,12 @@ namespace Crow /// IML fragment to parse /// type this item will be choosen for, or member of the data item /// for hierarchical data, method to call for children fetching - public ItemTemplate (Stream ImlFragment, string _dataType, string _fetchDataMethod) + public ItemTemplate (Stream ImlFragment, string _dataTest, string _dataType, string _fetchDataMethod) :base(ImlFragment) { strDataType = _dataType; fetchMethodName = _fetchDataMethod; + dataTest = _dataTest; } /// /// Initializes a new instance of the class using the opened XmlReader in args. @@ -84,11 +87,12 @@ namespace Crow /// XML reader positionned before or at the root node /// type this item will be choosen for, or member of the data item /// for hierarchical data, method to call for children fetching - public ItemTemplate (XmlReader reader, string _dataType = null, string _fetchDataMethod = null) + public ItemTemplate (XmlReader reader, string _dataTest = "TypeOf" , string _dataType = null, string _fetchDataMethod = null) :base(reader) { strDataType = _dataType; fetchMethodName = _fetchDataMethod; + dataTest = _dataTest; } #endregion @@ -108,7 +112,7 @@ namespace Crow Type tmpGrpType = typeof(TemplatedGroup); Type evtType = typeof(EventHandler); - PropertyInfo piData = tmpGrpType.GetProperty ("Data"); + //PropertyInfo piData = tmpGrpType.GetProperty ("Data"); MethodInfo evtInvoke = evtType.GetMethod ("Invoke"); ParameterInfo [] evtParams = evtInvoke.GetParameters (); @@ -120,41 +124,55 @@ namespace Crow //DM is bound to templatedGroup root (arg0) //arg1 is the sender of the expand event DynamicMethod dm = new DynamicMethod ("dyn_expand_" + fetchMethodName, - typeof (void), args, true); + typeof (void), args,typeof(TemplatedGroup), true); System.Reflection.Emit.Label gotoEnd; System.Reflection.Emit.Label ifDataIsNull; + System.Reflection.Emit.Label gotoItemsContainerNotFound; ILGenerator il = dm.GetILGenerator (256); il.DeclareLocal(typeof(GraphicObject)); gotoEnd = il.DefineLabel (); ifDataIsNull = il.DefineLabel (); + gotoItemsContainerNotFound = il.DefineLabel (); il.Emit (OpCodes.Ldarg_1);//load sender of expand event - - il.Emit(OpCodes.Ldstr, "List"); + il.Emit(OpCodes.Ldstr, "ItemsContainer");//load name to find il.Emit (OpCodes.Callvirt, CompilerServices.miFindByName); - il.Emit (OpCodes.Stloc_0); + il.Emit (OpCodes.Stloc_0);//save items container as loc0 - //check that 'Data' of list is not already set + //ensure ItemsContainer is not null il.Emit (OpCodes.Ldloc_0); - il.Emit (OpCodes.Callvirt, piData.GetGetMethod ()); - il.Emit (OpCodes.Brfalse, ifDataIsNull); - il.Emit (OpCodes.Br, gotoEnd); + il.Emit (OpCodes.Brfalse, gotoItemsContainerNotFound); + + //check that node is not already expanded + il.Emit (OpCodes.Ldarg_0);//push root TemplatedGroup into the stack + il.Emit (OpCodes.Ldarg_1); + il.Emit (OpCodes.Call, CompilerServices.miIsAlreadyExpanded); + il.Emit (OpCodes.Brtrue, gotoEnd); +// il.Emit (OpCodes.Ldloc_0); +// il.Emit (OpCodes.Callvirt, piData.GetGetMethod ()); +// il.Emit (OpCodes.Brfalse, ifDataIsNull); +// il.Emit (OpCodes.Br, gotoEnd); + +// il.MarkLabel(ifDataIsNull); - il.MarkLabel(ifDataIsNull); //copy the ref of ItemTemplates list TODO: maybe find another way to share it among the nodes? - FieldInfo fiTemplates = tmpGrpType.GetField("ItemTemplates"); - il.Emit (OpCodes.Ldloc_0); - il.Emit (OpCodes.Ldarg_0); - il.Emit (OpCodes.Ldfld, fiTemplates); - il.Emit (OpCodes.Stfld, fiTemplates); +// FieldInfo fiTemplates = tmpGrpType.GetField("ItemTemplates"); +// il.Emit (OpCodes.Ldloc_0); +// il.Emit (OpCodes.Ldarg_0); +// il.Emit (OpCodes.Ldfld, fiTemplates); +// il.Emit (OpCodes.Stfld, fiTemplates); //call 'fetchMethodName' from the dataSource to build the sub nodes list - il.Emit (OpCodes.Ldloc_0);//push 'List' (of sub nodes) into the stack - il.Emit (OpCodes.Ldarg_1);//get the dataSource of the sender - il.Emit (OpCodes.Callvirt, CompilerServices.miGetDataSource); + //il.Emit (OpCodes.Ldarg_0);//load root templatedGroop + + il.Emit (OpCodes.Ldarg_0);//push root TemplatedGroup into the stack + il.Emit (OpCodes.Ldarg_1);//load sender node of expand + il.Emit (OpCodes.Callvirt, CompilerServices.miGetDataSource);//get the dataSource of the sender + + if (fetchMethodName != "self") {//special keyword self allows the use of recurent list<<< if (dataType == null) { @@ -167,7 +185,15 @@ namespace Crow emitGetSubData(il, dataType); } //set 'return' from the fetch method as 'data' of the list - il.Emit (OpCodes.Callvirt, piData.GetSetMethod ()); + //il.Emit (OpCodes.Callvirt, piData.GetSetMethod ()); + il.Emit (OpCodes.Ldloc_0);//load second arg of loadPage, the sender node + il.Emit (OpCodes.Ldstr, dataTest);//load 3rd arg, dataTest kind on subitems + il.Emit (OpCodes.Callvirt, CompilerServices.miLoadPage); + il.Emit (OpCodes.Br, gotoEnd); + + il.MarkLabel(gotoItemsContainerNotFound); + il.EmitWriteLine("ItemsContainer not found in ItemTemplate for " + host.ToString()); + il.MarkLabel(gotoEnd); il.Emit (OpCodes.Ret); @@ -203,7 +229,6 @@ namespace Crow HasSubItems = (BooleanTestOnInstance)dm.CreateDelegate (typeof(BooleanTestOnInstance)); #endregion } - //data is on the stack void emitGetSubData(ILGenerator il, Type dataType){ MethodInfo miGetDatas = dataType.GetMethod (fetchMethodName, new Type[] {}); -- 2.47.3