Search code examples
codesmithuitypeeditor

Anyone have a UITypeEditor that picks System.Type instances?


I am busy writing a CodeSmith template that has one of its properties as type System.Type. I want to be able to select the type using a UI that picks the assembly, loads the assembly and then displays the types that are available in that assembly. I can then go and pick one of the types.

Has anyone encountered or written code that does this or something similar?


Solution

  • I don't have anything to hand, but it isn't hugely difficult to knock one up... the biggest gotcha is the issue of not unloading the dlls... but a crude example:

    (this uses the string AssemblyQualifiedName, but a Type works virtually identically - just change about 3 lines)

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Drawing.Design;
    using System.Reflection;
    using System.Windows.Forms;
    using System.Windows.Forms.Design;
    
    class MyData {
        [Editor(typeof(TypeTypeEditor), typeof(UITypeEditor))]
        [DisplayName("Some Type"), Description("Which type to use...")]
        public string SomeType { get; set; }
    
        [STAThread]
        static void Main() {
            Application.EnableVisualStyles();
            Application.Run(new Form {
                Controls = {
                    new PropertyGrid { 
                        Dock = DockStyle.Fill,
                        SelectedObject = new MyData()
                    }
                }
            });
        }
    }
    
    class TypeTypeEditor : UITypeEditor {
        public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context) {
            return UITypeEditorEditStyle.Modal;
        }
        public override object EditValue(ITypeDescriptorContext context, System.IServiceProvider provider, object value) {
            IWindowsFormsEditorService svc = provider == null ? null
                : provider.GetService(typeof(IWindowsFormsEditorService)) as IWindowsFormsEditorService;
            if (svc != null) {
                using(TypeForm form = new TypeForm()) {
                    form.TypeName = Convert.ToString(value);
                    if (svc.ShowDialog(form) == DialogResult.OK) {
                        value = form.TypeName;
                    }
                }
            }
            return value;
        }
    }
    class TypeForm : Form {
        public string TypeName { get; set; }
        Button ok, load;
        TreeView tree;
        public TypeForm() {
            Text = "Select type";
            ok = new Button { Text = "OK" };
            ok.Enabled = false;
            ok.DialogResult = DialogResult.OK;
            load = new Button { Text = "Load..." };
            load.Dock = ok.Dock = DockStyle.Bottom;
            this.AcceptButton = ok;
            tree = new TreeView();
            tree.Dock = DockStyle.Fill;
            load.Click += load_Click;
            Controls.Add(load);
            Controls.Add(ok);
            Controls.Add(tree);
            tree.AfterSelect += tree_AfterSelect;
        }
    
        void tree_AfterSelect(object sender, TreeViewEventArgs e) {
            ok.Enabled = false;
            if (e.Node != null && e.Node.Tag != null) {
                string s = Convert.ToString(e.Node.Tag);
                if (!string.IsNullOrEmpty(s)) {
                    TypeName = s;
                    ok.Enabled = true;
                }            
            }
        }
    
        void load_Click(object sender, EventArgs e) {
            try {
                string path = null;
                using (OpenFileDialog dlg = new OpenFileDialog()) {
                    dlg.Filter = "dll|*.dll|exe|*.exe";
                    if (dlg.ShowDialog(this) == DialogResult.OK) {
                        path = dlg.FileName;
                    }
                }
                if (!string.IsNullOrEmpty(path)) {
                    Assembly asm = Assembly.LoadFrom(path);
                    SortedList<string, TreeNode> namespaces = new SortedList<string, TreeNode>();
                    foreach (Type type in asm.GetTypes()) {
                        if (!type.IsPublic) continue;
                        TreeNode nsNode;
                        if (!namespaces.TryGetValue(type.Namespace, out nsNode)) {
                            nsNode = new TreeNode(type.Namespace);
                            namespaces.Add(type.Namespace, nsNode);
                        }
                        nsNode.Nodes.Add(type.Name).Tag = type.AssemblyQualifiedName;
                    }
                    tree.BeginUpdate();
                    tree.Nodes.Clear();
                    try {
                        foreach (TreeNode node in namespaces.Values) {
                            tree.Nodes.Add(node);
                        }
                    }
                    finally {
                        tree.EndUpdate();
                    }
                }
            }
            catch (Exception ex) {
                MessageBox.Show(this, ex.Message, ex.GetType().Name, MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }
    }