/// <summary> /// Split this up in to sections so I can more easliy re-use it in the future. /// </summary> /// <param name="className"></param> public static void Generate(string className, string outputAssemblyName, CompilerFlags flags, List <String> assemblies) { string prefix = (flags.IsAbstract ? ABSTRACT_PREFIX : String.Empty); //this *can* generate more than one entry ControlTree[] trees = GenerateTree(className, outputAssemblyName, assemblies); if (trees != null && trees.Length == 1) { ControlTree tree = trees[0]; string s = ClassGenerator(tree, flags); string infill = (flags.AppendDesignedToFilename ? ".Designer" : String.Empty); //add in the Designed string fileName = System.IO.Path.Combine(flags.OutputPath, String.Format("{1}{0}Controller{2}.cs", tree.ClassName, prefix, infill)); //added a check to see if file exists, otherwise we might get weird streaming issues //if it does, I delete it for now. if (File.Exists(fileName)) { File.Delete(fileName); } WriteFile(s, fileName); if (flags.CreateEmptyNonDesignedFile) { string stubFileName = System.IO.Path.Combine(flags.OutputPath, String.Format("{1}{0}Controller.cs", tree.ClassName, prefix)); if (!System.IO.File.Exists(stubFileName)) { WriteFile(CreateStubFile(tree, flags), stubFileName); } } } }
/// <summary> /// This fixes the subcontrol issues /// </summary> private static void IterateTree(System.Windows.Forms.Control targetControl, ControlTree tree, string path) { Console.WriteLine(path); foreach (System.Windows.Forms.Control control in targetControl.Controls) { //this works around the fact that the toolStrip does weird stuff with named controls if (control.Name != null && control.Name != String.Empty) { tree.AddControl(control.Name, control.GetType()); } if (control.Controls.Count > 0) { IterateTree(control, tree, String.Format("{0}_{1}", path, control.Name)); //recurse.... } //special case - controlstrip holds its own components if (control is System.Windows.Forms.StatusStrip) { foreach (System.Windows.Forms.ToolStripItem item in (control as System.Windows.Forms.StatusStrip).Items) { if (item is System.Windows.Forms.ToolStripItem) { var name = (item as System.Windows.Forms.ToolStripItem).Name; tree.AddControl((name == null || name == String.Empty ? "Blah" : name), item.GetType()); } } } } }
/// <summary> /// Builds a tree of control info /// </summary> /// <param name="className"></param> public static ControlTree[] GenerateTree(string className, string outputAssemblyName) { List <ControlTree> trees = new List <ControlTree>(); Assembly a = Assembly.LoadFrom(outputAssemblyName /*"temp.dll"*/); //we dictate this name Type[] ts = a.GetTypes(); foreach (Type t in ts) { if (t.Name.Equals(className)) { ControlTree tree = new ControlTree(); trees.Add(tree); //just to get it sorted.. //essentially, we only support "Winforms" so we assume it is a form or descends from one //I might be able to alter this to "component" I gues... System.Windows.Forms.Form form = (Activator.CreateInstance(t) as System.Windows.Forms.Form); tree.ClassName = t.Name; tree.NamespaceName = t.Namespace; ////we now have access to the controls //foreach (System.Windows.Forms.Control control in form.Controls) //{ // tree.AddControl(control.Name, control.GetType()); //} IterateTree(form, tree, className); //add in the components that are non visual and in the container on the form IterateComponents(form, tree, className); } } return(trees.ToArray()); }
private static void IterateDropDownItems(ControlTree tree, System.Windows.Forms.ToolStripDropDownItem item) { foreach (var menuitem in item.DropDownItems) { var name = (menuitem as System.Windows.Forms.ToolStripItem).Name; tree.AddControl((name == null || name == String.Empty ? "Blah" : name), item.GetType()); if (menuitem is System.Windows.Forms.ToolStripDropDownItem) { IterateDropDownItems(tree, (System.Windows.Forms.ToolStripDropDownItem)menuitem); } } }
/// <summary> /// Creates a stub file with the same makeup of the main file /// </summary> /// <param name="tree"></param> /// <param name="flags"></param> /// <returns></returns> private static string CreateStubFile(ControlTree tree, CompilerFlags flags) { string prefix = (flags.IsAbstract ? ABSTRACT_PREFIX : String.Empty); StringBuilder code = new StringBuilder(); code.AppendLine("/*This stub code was generated by the MvcFramework compiler, created by RatCow Soft - \r\n See http://code.google.com/p/ratcowsoftopensource/ */ \r\n\r\nusing System; \r\nusing System.Windows.Forms;\r\nusing System.Collections.Generic;\r\nusing System.Linq;\r\nusing System.Text;\r\nusing System.Reflection;\r\n\r\n//3rd Party\r\nusing RatCow.MvcFramework;\r\n"); code.AppendFormat("namespace {0}\r\n", tree.NamespaceName); code.AppendLine("{"); code.AppendFormat("\tinternal partial class {1}{0}Controller: BaseController<{0}>\r\n", tree.ClassName, prefix); code.AppendLine("\t{"); code.AppendLine("\t}"); code.AppendLine("}"); return(code.ToString()); }
/// <summary> /// Hack around the fact we don't have any access to the stuff in container on the form. /// /// Here's the issue: Winforms have a private components collection. This contains a reference /// to all the components on the form. It's trivial to get access to this - to be honest, I went /// for the simple hack, but it would probably have worked with the right BindingFlags params /// no matter what. But here's the thing - Components don't have a name.... /// /// So how can we pull through the name? Well, I could completely refactor all of the code to /// look at the fields of the form and pull out all of the controls and components, but to be honest, /// that seems like a faff. So what I instead did was this - we know the field is a reference, and the /// same reference is in the components collection. Only the stuff we want is ref'd in the components /// collection. So, if we iterate through the components collection, get the ref and then cross /// ref that reference with the fields on the folrm (using a bit of Linq to reduce the list by type) /// we can then do the look-up cross ref and get the fields name. /// /// I'm not sure if there's an easier way, but this works for now. /// </summary> private static void IterateComponents(System.Windows.Forms.Form form, ControlTree tree, string className) { //the componments are in a private list (so we need hackery to access it) //when we compiled the class, we added in a public accessor called "ContainerAccess" //This of course doesn't work very well.... but it does give us a handle on the component //types we should be looking for... Type formType = form.GetType(); var accessor = formType.GetProperty("ComponentsAccess"); if (accessor != null) { var payload = accessor.GetValue(form, null); System.ComponentModel.IContainer container = (System.ComponentModel.IContainer)payload; if (container == null) { return; } foreach (System.ComponentModel.Component component in container.Components) { //we must now look for component in the fields with in the form, to look up the name Type fieldType = component.GetType(); //we use this to match types FieldInfo[] fia = formType.GetFields(BindingFlags.Default | BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.Instance); //limit to just the same calss type var fiar = from field in fia where field.FieldType == fieldType select field; if (fiar == null) { continue; //this is no ideal } //iterate the resultset, hoping we find at least one value and can match it. foreach (var fi in fiar) { var instance = fi.GetValue(form); if (instance == component) { tree.AddControl(fi.Name, fieldType); } } } } }
private static void ContextMenuStrip_IterateToolStripItems(ControlTree tree, System.Windows.Forms.Control control) { foreach (System.Windows.Forms.ToolStripItem item in (control as System.Windows.Forms.ContextMenuStrip).Items) { if (item is System.Windows.Forms.ToolStripItem) { var name = (item as System.Windows.Forms.ToolStripItem).Name; tree.AddControl((name == null || name == String.Empty ? "Blah" : name), item.GetType()); if (item is System.Windows.Forms.ToolStripDropDownItem) { var item2 = (System.Windows.Forms.ToolStripDropDownItem)item; IterateDropDownItems(tree, item2); } } } }
/// <summary> /// Builds a tree of control info /// </summary> /// <param name="className"></param> public static ControlTree[] GenerateTree(string className, string outputAssemblyName, List <String> assemblies) { List <ControlTree> trees = new List <ControlTree>(); var resolver = new AssemblyResolver(assemblies); //this *should* reslve the missing assemblies for us Assembly a = Assembly.LoadFrom(outputAssemblyName /*"temp.dll"*/); //we dictate this name Type[] ts = a.GetTypes(); foreach (Type t in ts) { if (t.Name.Equals(className)) { ControlTree tree = new ControlTree(); trees.Add(tree); //just to get it sorted.. //essentially, we only support "Winforms" so we assume it is a form or descends from one //I might be able to alter this to "component" I guess... try { System.Windows.Forms.Form form = (Activator.CreateInstance(t) as System.Windows.Forms.Form); tree.ClassName = t.Name; tree.NamespaceName = t.Namespace; ////we now have access to the controls //foreach (System.Windows.Forms.Control control in form.Controls) //{ // tree.AddControl(control.Name, control.GetType()); //} IterateTree(form, tree, className); //add in the components that are non visual and in the container on the form IterateComponents(form, tree, className); } catch (Exception ex) //todo: handle this better. { //okay... we have failed big time, so we just bail System.Diagnostics.Debug.WriteLine(ex.Message); System.Diagnostics.Debug.WriteLine(ex.StackTrace); return(null); } } } return(trees.ToArray()); }
/// <summary> /// /// </summary> /// <param name="tree"></param> /// <param name="isAbstract"></param> /// <returns></returns> public static string ClassGenerator(ControlTree tree, CompilerFlags flags) { string prefix = (flags.IsAbstract ? ABSTRACT_PREFIX : String.Empty); StringBuilder code = new StringBuilder(); code.AppendLine("/*Auto generated - this code was generated by the MvcFramework compiler, created by RatCow Soft - \r\n See http://code.google.com/p/ratcowsoftopensource/ */ \r\n\r\nusing System; \r\nusing System.Windows.Forms;\r\nusing System.Collections.Generic;\r\nusing System.Linq;\r\nusing System.Text;\r\nusing System.Reflection;\r\n\r\n//3rd Party\r\nusing RatCow.MvcFramework;\r\n"); code.AppendFormat("namespace {0}\r\n", tree.NamespaceName); code.AppendLine("{"); StringBuilder code_s1 = new StringBuilder(); if (flags.CreateEmptyNonDesignedFile) { //if we are creating a stub, we should put the inheritence in the stub, to allow it to be altered (e.g. modal) code_s1.AppendFormat("\tinternal partial class {1}{0}Controller\r\n", tree.ClassName, prefix); } else { code_s1.AppendFormat("\tinternal partial class {1}{0}Controller: BaseController<{0}>\r\n", tree.ClassName, prefix); } code_s1.AppendLine("\t{"); //constructor code_s1.AppendFormat("\t\tpublic {1}{0}Controller() : base()\r\n", tree.ClassName, prefix); code_s1.AppendLine("\t\t{\r\n\t\t}\r\n"); StringBuilder code_s2 = new StringBuilder(); code_s2.AppendLine("\r\n#region GUI glue code\r\n"); code_s2.AppendFormat("\tpartial class {1}{0}Controller\r\n", tree.ClassName, prefix); code_s2.AppendLine("\t{"); if (flags.RestrictActions) { //load the actions file ViewActionMap map = (flags.UseDefaultActionsFile ? GetDefaultViewActionMap() : GetNamedViewActionMap(tree.ClassName, true)); //we now have access to the controls foreach (var control in tree.Controls) { //System.Console.WriteLine(" var {0} : {1} ", control.Name, control.GetType().Name); //add the declaration to code_s2 code_s2.AppendFormat("\t\t[Outlet(\"{1}\")]\r\n\t\tpublic {0} {1} ", control.Value.FullName, control.Key); //add var code_s2.AppendLine("{ get; set; }"); //this should find all known event types AddMappedActions(tree.ClassName, control, code_s2, code_s1, flags, map); //specific listView hooks if (control.Value == typeof(System.Windows.Forms.ListView)) { AddListViewHandler(control.Key, code_s2, code_s1, flags); } } } else { //we now have access to the controls foreach (var control in tree.Controls) { //System.Console.WriteLine(" var {0} : {1} ", control.Name, control.GetType().Name); //add the declaration to code_s2 code_s2.AppendFormat("\t\t[Outlet(\"{1}\")]\r\n\t\tpublic {0} {1} ", control.Value.FullName, control.Key); //add var code_s2.AppendLine("{ get; set; }"); //this should find all known event types AddKnownActions(tree.ClassName, control, code_s2, code_s1, flags); //specific listView hooks if (control.Value == typeof(System.Windows.Forms.ListView)) { AddListViewHandler(control.Key, code_s2, code_s1, flags); } } } //some boiler plate code which helps set the data for a LisViewHelper code_s2.AppendLine("\t\tprotected void SetData<T>(ListViewHelper<T> helper, List<T> data) where T : class"); code_s2.AppendLine("\t\t{\r\n\t\t\t//Auto generated call"); code_s2.AppendLine("\t\t\tType t = helper.GetType();"); code_s2.AppendLine("\t\t\tt.InvokeMember(\"SetData\", BindingFlags.Default | BindingFlags.InvokeMethod, null, helper, new object[] { data });"); code_s2.AppendLine("\t\t}\r\n"); code_s1.AppendLine("\t}"); //end of class declaration code.AppendLine(code_s1.ToString()); code_s2.AppendLine("\t}"); //end of class declaration code_s2.AppendLine("#endregion /*GUI glue code*/"); code.AppendLine(code_s2.ToString()); code.AppendLine("}"); return(code.ToString()); }