Display Windows GUI for command line parameters
<?xml version="1.0" encoding="utf-8"?> <xsharper xmlns="http://www.xsharper.com/schemas/1.0" unknownSwitches="true"> <versionInfo title="gui-console" value="Run XSharper script requesting parameters in GUI." Version="0.1.0.0" Copyright="(C) 2009 DeltaX Inc." /> <usage options="ifNoArguments default" /> <param switch="run" required="false" value="Run immediately after loading" count="none" /> <param name="filename" required="true" value="Script to execute" description="filename.xsh" /> <param name="args" required="false" value="Command line arguments for the script" count="multiple" description="arguments" last="true" /> <include id="myScript" from="${filename}" dynamic="true" /> <call subId="run-in-gui-param"> <param>${=$~myScript.IncludedScript}</param> <param>${=.QuoteArgs(${args|=null})}</param> </call> <sub id="run-in-gui-param"> <param name="script" required="true" /> <param name="scriptArgs" required="true" /> <?_ Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); ExecutorForm f1=new ExecutorForm(); f1.Context=c; if (c["script"] is XS.Script) f1.Script=(XS.Script)c["script"]; else f1.Script=c.Find<XS.Script>(c.GetString("script"),true); f1.Args=c.GetString("scriptArgs"); f1.Autorun=c.GetBool("run",false); Application.Run(f1); ?> <reference name="System.Windows.Forms" /> <reference name="System.Drawing" /> <?header using System; using System.Drawing.Design; using System.Windows.Forms; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Drawing; using System.IO; using System.Text; using XS=XSharper.Core; using System.Runtime.InteropServices; using System.Threading; public class ExecutorForm : Form { private ManualResetEvent _running = new ManualResetEvent(false); private ManualResetEvent _stopEvent = new ManualResetEvent(false); private bool _closeOnStop = false; public XS.ScriptContext Context; public XS.Script Script; public string Args; public bool Autorun; public ExecutorForm() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { KeyPreview = true; this.Icon = Icon.ExtractAssociatedIcon(Process.GetCurrentProcess().MainModule.FileName); // Change font & tab stops reOut.LoadFonts(); lblDescription.Text = Script.VersionInfo.GenerateInfo(Context, true); string title = Context.TransformStr(Script.VersionInfo.Title, Script.VersionInfo.Transform); if (string.IsNullOrEmpty(title)) title = Script.Id; if (string.IsNullOrEmpty(title)) title = Script.Location; if (string.IsNullOrEmpty(title)) Text = "XSharper"; else Text = title + " - XSharper"; edArgs.Text = Args ?? ""; edArgs.SelectionStart = 0; edArgs.SelectionLength = 0; // CustomPropertyCollection c = new CustomPropertyCollection(); string cat = null; int catNo = 0; foreach (XS.CommandLineParameter p in Script.Parameters) { if (string.IsNullOrEmpty(p.Var)) { if (!string.IsNullOrEmpty(p.Value)) { cat = p.Value; catNo++; } continue; } if (cat == null) cat = "Parameters:"; c.Add(createProperty(p, Context, string.Concat((char)31, (char)(catNo + 32), cat))); } propertyGrid.SelectedObject = c; propertyGrid.SetRatio(4); propertyGrid.PropertyValueChanged += delegate { updateCommandLine(); }; edArgs.Validating += delegate(object snder, CancelEventArgs ex) { ex.Cancel = !updatePropertyGrid(); }; updatePropertyGrid(); if (Autorun) BeginInvoke((MethodInvoker)delegate() { click(); }); } private static CustomProperty createProperty(XS.CommandLineParameter p, XS.ScriptContext context, string category) { CustomProperty ret = new CustomProperty(); ret.DisplayName = p.GetDescription(context); ret.Description = p.GetTransformedValue(context); ret.Category = category; string typename = context.TransformStr(p.TypeName, p.Transform); if (p.Count == XS.CommandLineValueCount.Multiple) ret.PropertyType = typeof(string[]); else ret.PropertyType = string.IsNullOrEmpty(typename) ? typeof(string) : (context.FindType(typename) ?? typeof(string)); if (p.Default != null) ret.Value = ret.DefaultValue = XS.Utils.To(ret.PropertyType, context.Transform(p.Default, p.Transform)); else if (ret.PropertyType.IsValueType && !p.Required) { if (!(ret.PropertyType.IsGenericType && ret.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>))) ret.PropertyType = typeof (Nullable<>).MakeGenericType(ret.PropertyType); } if (ret.PropertyType == typeof(string)) { List<string> possV = new List<string>(); bool regex = false; if (!string.IsNullOrEmpty(p.Pattern) && p.Pattern.StartsWith("^(") && p.Pattern.EndsWith(")$")) { regex = true; foreach (string s in p.Pattern.Substring(2, p.Pattern.Length - 4).Split('|')) if (System.Text.RegularExpressions.Regex.IsMatch(s,"[^a-zA-Z0-9_]")) regex=false; if (regex) foreach (string s in p.Pattern.Substring(2, p.Pattern.Length - 4).Split('|')) { string un = System.Text.RegularExpressions.Regex.Unescape(s); possV.Add(un); } } if (ret.DefaultValue != null) possV.Add(XS.Utils.To<string>(ret.DefaultValue)); else if (!p.Required && !possV.Contains(null)) possV.Add(null); if (p.Unspecified != null) { string v = XS.Utils.To<string>(context.Transform(p.Unspecified, p.Transform)); if (!possV.Contains(v)) possV.Add(v); } if (possV.Count != 0) { ret.StandardValues = possV.ToArray(); ret.OnlyStandardValues = (p.Count == XS.CommandLineValueCount.None || regex); } } ret.Tag = p; return ret; } bool updatePropertyGrid() { try { using (new XS.ScriptContextScope(Context)) { XS.CommandLineParameters c = new XS.CommandLineParameters(Script.Parameters, Script.SwitchPrefixes, Script.UnknownSwitches); c.Parse(Context, XS.Utils.SplitArgs(edArgs.Text), false); c.ApplyDefaultValues(Context); } foreach (CustomProperty p in (CustomPropertyCollection)propertyGrid.SelectedObject) { XS.CommandLineParameter pp = (XS.CommandLineParameter)p.Tag; if (Context.IsSet(pp.Var)) p.Value = XS.Utils.To(p.PropertyType, Context[pp.Var]); else p.Value = null; } propertyGrid.Refresh(); return true; } catch (Exception ee) { MessageBox.Show(ee.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); return false; } } static bool isDefault(CustomProperty p) { XS.CommandLineParameter pp = (XS.CommandLineParameter)p.Tag; return !(p.Value != null && (pp.Required || p.DefaultValue == null || !XS.Utils.To(p.PropertyType, p.Value).Equals(XS.Utils.To(p.PropertyType, p.DefaultValue)))); } void updateCommandLine() { List<XS.ShellArg> args = new List<XS.ShellArg>(); CustomPropertyCollection coll=(CustomPropertyCollection)propertyGrid.SelectedObject; for (int i=0;i<coll.Count;++i) { CustomProperty p =coll[i]; XS.CommandLineParameter pp = (XS.CommandLineParameter)p.Tag; if (isDefault(p)) { if (string.IsNullOrEmpty(pp.Switch)) { for (int j=i+1;j<coll.Count;++j) if (string.IsNullOrEmpty(((XS.CommandLineParameter)coll[j].Tag).Switch) && !isDefault(coll[j])) { XS.ShellArg a = new XS.ShellArg(); args.Add(a); if (pp.Count != XS.CommandLineValueCount.None) a.Value = p.Value; a.Transform = XS.TransformRules.None; } } } else { XS.ShellArg a = new XS.ShellArg(); if (!string.IsNullOrEmpty(pp.Switch)) { if (!string.IsNullOrEmpty(this.Script.SwitchPrefixes)) a.Switch = Script.SwitchPrefixes[0] + pp.Switch; else a.Switch = pp.Switch; } if (pp.Count != XS.CommandLineValueCount.None) a.Value = p.Value; a.Transform = XS.TransformRules.None; args.Add(a); } } edArgs.Text = XS.ShellArg.GetCommandLine(Context, args); } void btnRun_Click(object sender, EventArgs e) { click(); } void click() { if (_running.WaitOne(0, false)) { _stopEvent.Set(); return; } Stopwatch sw = Stopwatch.StartNew(); object ret = null; string oldText = btnRun.Text; string oldDir = Directory.GetCurrentDirectory(); EventHandler<XS.OutputEventArgs> oldOut = Context.Output; EventHandler<XS.OperationProgressEventArgs> oldProgress = Context.Progress; try { _stopEvent.Reset(); _running.Set(); btnRun.Text = "&Cancel"; btnRun.Update(); reOut.Clear(); Context.MinOutputType = chkDebug.Checked ? XS.OutputType.Debug : XS.OutputType.Info; Context.Output = OnOutput; Cursor = Cursors.WaitCursor; Context.Progress = delegate(object sender1, XS.OperationProgressEventArgs e1) { Application.DoEvents(); if (_stopEvent.WaitOne(0, false)) { e1.Cancel = true; _stopEvent.Reset(); } }; using (XS.ConsoleRedirector r = new XS.ConsoleRedirector(Context)) { Context.In = TextReader.Null; ret = Context.ExecuteScript(Script, XS.Utils.SplitArgs(edArgs.Text), XS.CallIsolation.High); } } catch (Exception ex) { Context.WriteException(ex); ret = -1; } Directory.SetCurrentDirectory(oldDir); _running.Reset(); btnRun.Text = oldText; Context.Info.WriteLine("--- Completed in " + sw.Elapsed + " with return value=" + ret + " ---"); reOut.ScrollToBottom(); Context.Progress = oldProgress; Context.Output = oldOut; Cursor = Cursors.Arrow; Context.Progress = null; Context.Output = null; if (_closeOnStop) Close(); } private void OnOutput(object sender1, XS.OutputEventArgs e1) { reOut.Output(e1.OutputType, e1.Text); } private void Form1_FormClosing(object sender, FormClosingEventArgs e) { if (_running.WaitOne(0, false)) { Text = "Cancelling script..."; _stopEvent.Set(); _closeOnStop = true; e.Cancel = true; return; } } /// <summary> /// Required designer variable. /// </summary> private System.ComponentModel.IContainer components = null; /// <summary> /// Clean up any resources being used. /// </summary> /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param> protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code /// <summary> /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// </summary> private void InitializeComponent() { this.splitContainer1 = new System.Windows.Forms.SplitContainer(); this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); this.label2 = new System.Windows.Forms.Label(); this.labelGrid = new System.Windows.Forms.Label(); this.propertyGrid = new PropertyGridEx(); this.btnRun = new System.Windows.Forms.Button(); this.chkDebug = new System.Windows.Forms.CheckBox(); this.lblDescription = new System.Windows.Forms.Label(); this.label1 = new System.Windows.Forms.Label(); this.edArgs = new System.Windows.Forms.TextBox(); this.reOut = new OutputRichTextBox(); this.splitContainer1.Panel1.SuspendLayout(); this.splitContainer1.Panel2.SuspendLayout(); this.splitContainer1.SuspendLayout(); this.tableLayoutPanel1.SuspendLayout(); this.SuspendLayout(); // // splitContainer1 // this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill; this.splitContainer1.Location = new System.Drawing.Point(0, 0); this.splitContainer1.Name = "splitContainer1"; this.splitContainer1.Orientation = System.Windows.Forms.Orientation.Horizontal; // // splitContainer1.Panel1 // this.splitContainer1.Panel1.Controls.Add(this.tableLayoutPanel1); // // splitContainer1.Panel2 // this.splitContainer1.Panel2.Controls.Add(this.reOut); this.splitContainer1.Size = new System.Drawing.Size(800, 550); this.splitContainer1.SplitterDistance = 390; this.splitContainer1.SplitterWidth = 5; this.splitContainer1.TabIndex = 0; // // tableLayoutPanel1 // this.tableLayoutPanel1.ColumnCount = 3; this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); this.tableLayoutPanel1.Controls.Add(this.label2, 0, 0); this.tableLayoutPanel1.Controls.Add(this.labelGrid, 0, 1); this.tableLayoutPanel1.Controls.Add(this.propertyGrid, 1, 1); this.tableLayoutPanel1.Controls.Add(this.btnRun, 2, 1); this.tableLayoutPanel1.Controls.Add(this.chkDebug, 2, 0); this.tableLayoutPanel1.Controls.Add(this.lblDescription, 1, 0); this.tableLayoutPanel1.Controls.Add(this.label1, 0, 2); this.tableLayoutPanel1.Controls.Add(this.edArgs, 1, 2); this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); this.tableLayoutPanel1.Name = "tableLayoutPanel1"; this.tableLayoutPanel1.Padding = new System.Windows.Forms.Padding(3); this.tableLayoutPanel1.RightToLeft = System.Windows.Forms.RightToLeft.No; this.tableLayoutPanel1.RowCount = 3; this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 80F)); this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); this.tableLayoutPanel1.Size = new System.Drawing.Size(800, 390); this.tableLayoutPanel1.TabIndex = 0; // // label2 // this.label2.Location = new System.Drawing.Point(6, 3); this.label2.Name = "label2"; this.label2.Size = new System.Drawing.Size(100, 23); this.label2.TabIndex = 0; this.label2.Text = "Script description:"; this.label2.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; // // labelGrid // this.labelGrid.Location = new System.Drawing.Point(6, 26); this.labelGrid.Name = "labelGrid"; this.labelGrid.Size = new System.Drawing.Size(100, 23); this.labelGrid.TabIndex = 2; this.labelGrid.Text = "&Parameters:"; this.labelGrid.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; // // propertyGrid // this.propertyGrid.Dock = System.Windows.Forms.DockStyle.Fill; this.propertyGrid.Location = new System.Drawing.Point(112, 29); this.propertyGrid.Name = "propertyGrid"; this.propertyGrid.Size = new System.Drawing.Size(601, 329); this.propertyGrid.TabIndex = 3; this.propertyGrid.ToolbarVisible = false; // // btnRun // this.btnRun.DialogResult = System.Windows.Forms.DialogResult.OK; this.btnRun.Dock = System.Windows.Forms.DockStyle.Top; this.btnRun.Location = new System.Drawing.Point(719, 29); this.btnRun.Name = "btnRun"; this.btnRun.Size = new System.Drawing.Size(75, 21); this.btnRun.TabIndex = 7; this.btnRun.Text = "&Run"; this.btnRun.UseVisualStyleBackColor = true; this.btnRun.Click += new System.EventHandler(this.btnRun_Click); // // chkDebug // this.chkDebug.AutoSize = true; this.chkDebug.Dock = System.Windows.Forms.DockStyle.Bottom; this.chkDebug.Location = new System.Drawing.Point(719, 6); this.chkDebug.Name = "chkDebug"; this.chkDebug.Size = new System.Drawing.Size(75, 17); this.chkDebug.TabIndex = 6; this.chkDebug.Text = "&Debug"; this.chkDebug.UseVisualStyleBackColor = true; // // lblDescription // this.lblDescription.AccessibleRole = System.Windows.Forms.AccessibleRole.Document; this.lblDescription.AutoSize = true; this.lblDescription.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D; this.lblDescription.Dock = System.Windows.Forms.DockStyle.Fill; this.lblDescription.Location = new System.Drawing.Point(112, 3); this.lblDescription.Name = "lblDescription"; this.lblDescription.Size = new System.Drawing.Size(601, 23); this.lblDescription.TabIndex = 1; this.lblDescription.Text = "..."; this.lblDescription.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; // // label1 // this.label1.Location = new System.Drawing.Point(6, 361); this.label1.Name = "label1"; this.label1.Size = new System.Drawing.Size(95, 26); this.label1.TabIndex = 4; this.label1.Text = "&Command line:"; this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; // // edArgs // this.edArgs.Dock = System.Windows.Forms.DockStyle.Fill; this.edArgs.Location = new System.Drawing.Point(112, 364); this.edArgs.Name = "edArgs"; this.edArgs.Size = new System.Drawing.Size(601, 20); this.edArgs.TabIndex = 5; // // reOut // this.reOut.BackColor = System.Drawing.Color.Black; this.reOut.Dock = System.Windows.Forms.DockStyle.Fill; this.reOut.Font = new System.Drawing.Font("Courier New", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(204))); this.reOut.ForeColor = System.Drawing.Color.White; this.reOut.Location = new System.Drawing.Point(0, 0); this.reOut.Name = "reOut"; this.reOut.ReadOnly = true; this.reOut.RightToLeft = System.Windows.Forms.RightToLeft.No; this.reOut.Size = new System.Drawing.Size(800, 155); this.reOut.TabIndex = 0; this.reOut.Text = ""; // // ExecutorForm // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(800, 550); this.Controls.Add(this.splitContainer1); this.Name = "ExecutorForm"; this.Text = " Sample Script Executor"; this.Load += new System.EventHandler(this.Form1_Load); this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.Form1_FormClosing); this.splitContainer1.Panel1.ResumeLayout(false); this.splitContainer1.Panel2.ResumeLayout(false); this.splitContainer1.ResumeLayout(false); this.tableLayoutPanel1.ResumeLayout(false); this.tableLayoutPanel1.PerformLayout(); this.ResumeLayout(false); } #endregion private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; private OutputRichTextBox reOut; private System.Windows.Forms.Button btnRun; private System.Windows.Forms.CheckBox chkDebug; private System.Windows.Forms.Label label2; private System.Windows.Forms.TextBox edArgs; private System.Windows.Forms.Label label1; private System.Windows.Forms.Label labelGrid; private System.Windows.Forms.Label lblDescription; private PropertyGridEx propertyGrid; private System.Windows.Forms.SplitContainer splitContainer1; } // Extended richedit box, that supports different fonts and backspace character public class OutputRichTextBox : RichTextBox { private Font _font, _fontBold; [DllImport("User32.dll", CharSet = CharSet.Auto)] private static extern int SendMessage(IntPtr h, int msg, int wParam, int lParam); private Font createFont(bool bold) { Font f = new Font("Consolas", 9, bold ? FontStyle.Bold : FontStyle.Regular); if (f.Name != "Consolas") f = new Font("Courier New", 9, bold ? FontStyle.Bold : FontStyle.Regular); return f; } public void LoadFonts() { _font = createFont(false); _fontBold = createFont(true); this.Font = _font; } public void ScrollToBottom() { SendMessage(this.Handle, 0x115, 7, 0); } public void Output(XS.OutputType otype, string text) { this.Select(this.TextLength, 0); StringBuilder sb = new StringBuilder(); foreach (char ch in text) { if (ch == (char)8) { if (sb.Length > 0) this.AppendText(sb.ToString()); while (this.TextLength > 0) { this.Select(this.TextLength - 1, 1); string s = this.SelectedText; if (s == "\n" || s == "\r" || s == "") { this.Select(this.TextLength, 0); break; } this.Select(this.TextLength - 1, 1); this.ReadOnly = false; this.SelectedText = string.Empty; this.ReadOnly = true; } sb.Length = 0; } else { if (sb.Length == 0) { switch (otype) { case XS.OutputType.Debug: this.SelectionColor = Color.Cyan; break; case XS.OutputType.Error: this.SelectionColor = Color.Yellow; break; case XS.OutputType.Info: this.SelectionColor = Color.LightGreen; break; case XS.OutputType.Bold: this.SelectionColor = Color.White; break; default: this.SelectionColor = Color.LightGray; break; } if (otype == XS.OutputType.Bold) { if (this.SelectionFont.Bold != true) this.SelectionFont = _fontBold; } else if (this.SelectionFont.Bold != false) this.SelectionFont = _font; } sb.Append(ch); if (sb.Length > 5000) { this.AppendText(sb.ToString()); sb.Length = 0; } } } this.AppendText(sb.ToString()); ScrollToBottom(); Application.DoEvents(); } } #region -- Extended property grid -- public class PropertyGridEx : PropertyGrid { private const int WM_KEYDOWN = 0x100; private const int TAB = 9; private bool _setEvent; private double _ratio; private Control _propertyGridView; public PropertyGridEx() { } protected override bool ProcessKeyPreview(ref Message m) { if (m.Msg == WM_KEYDOWN && m.WParam.ToInt32() == TAB) { bool forward = (ModifierKeys & Keys.Shift) == 0; if (moveSelectedGridItem(forward)) return true; } return ProcessKeyEventArgs(ref m); } protected override bool ProcessTabKey(bool forward) { moveSelectedGridItem(forward); return true; } public void SetRatio(double ratio) { _ratio = ratio; _propertyGridView = Controls[2]; if (!_setEvent) { _setEvent = true; _propertyGridView.VisibleChanged += delegate { setRatio(); }; _propertyGridView.SizeChanged += delegate { setRatio(); }; } setRatio(); } private void setRatio() { try { if (_propertyGridView != null) { Type propertyGridViewType = _propertyGridView.GetType(); System.Reflection.FieldInfo fldLabelRatio = propertyGridViewType.GetField("labelRatio", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public); if (fldLabelRatio != null) { fldLabelRatio.SetValue(_propertyGridView, _ratio); Invalidate(); } } } catch { } } private bool moveSelectedGridItem(bool forward) { if (SelectedGridItem == null || SelectedGridItem.Parent == null) return false; GridItem p = SelectedGridItem.Parent.Parent; GridItemCollection allItems = SelectedGridItem.Parent.GridItems; int currentIndex = -1; for (int i = 0; i < allItems.Count; i++) if (allItems[i] == SelectedGridItem) { currentIndex = i; break; } if (forward) { if (currentIndex >= 0 && currentIndex < allItems.Count - 1) { SelectedGridItem = allItems[currentIndex + 1]; return false; } if (currentIndex == allItems.Count - 1 && p != null) { GridItemCollection pitems = p.GridItems; for (int j = 0; j < pitems.Count - 1; j++) if (pitems[j] == SelectedGridItem.Parent) { SelectedGridItem = pitems[j + 1].GridItems[0]; return false; } Parent.SelectNextControl(this, forward, true, false, true); return false; } } else { if (currentIndex == 0 && p != null) { GridItemCollection pitems = p.GridItems; for (int j = 1; j < pitems.Count; j++) if (pitems[j] == SelectedGridItem.Parent) { SelectedGridItem = pitems[j - 1].GridItems[pitems[j - 1].GridItems.Count - 1]; return false; } Parent.SelectNextControl(this, forward, true, false, true); } else if (currentIndex >= 1 && currentIndex < allItems.Count) { SelectedGridItem = allItems[currentIndex - 1]; return true; } } return true; } } public class CustomPropertyCollection : List<CustomProperty>, ICustomTypeDescriptor { public AttributeCollection GetAttributes() { return TypeDescriptor.GetAttributes(this, true); } public string GetClassName() { return TypeDescriptor.GetClassName(this, true); } public string GetComponentName() { return TypeDescriptor.GetComponentName(this, true); } public TypeConverter GetConverter() { return new PropertySorter(ConvertAll(delegate(CustomProperty x) { return x.DisplayName; }).ToArray()); } public EventDescriptor GetDefaultEvent() { return TypeDescriptor.GetDefaultEvent(this, true); } public PropertyDescriptor GetDefaultProperty() { return TypeDescriptor.GetDefaultProperty(this, true); } public object GetEditor(System.Type editorBaseType) { return TypeDescriptor.GetEditor(this, editorBaseType, true); } public EventDescriptorCollection GetEvents() { return TypeDescriptor.GetEvents(this, true); } public EventDescriptorCollection GetEvents(System.Attribute[] attributes) { return TypeDescriptor.GetEvents(this, attributes, true); } public PropertyDescriptorCollection GetProperties() { return GetProperties(null); } public object GetPropertyOwner(PropertyDescriptor pd) { return this; } public PropertyDescriptorCollection GetProperties(System.Attribute[] attributes) { return new PropertyDescriptorCollection(ConvertAll(delegate(CustomProperty x) { return x.CreateDescriptor(); }).ToArray()); } #region -- private classes -- class PropertySorter : ExpandableObjectConverter { private readonly string[] _names; public PropertySorter(string[] names) { _names = names; } public override bool GetPropertiesSupported(ITypeDescriptorContext context) { return true; } public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes) { return TypeDescriptor.GetProperties(value, attributes).Sort(_names); } } #endregion } public class CustomProperty { public object Tag ; public string DisplayName ; public bool IsReadOnly ; public object Value ; public string Description ; public string Category ; public bool Parenthesize ; public bool IsPassword ; public bool IsPercentage ; public Type PropertyType ; public object DefaultValue ; public object[] StandardValues ; public bool OnlyStandardValues ; public PropertyDescriptor CreateDescriptor() { List<Attribute> attrs = new List<Attribute>(); if (IsPassword) attrs.Add(new PasswordPropertyTextAttribute(true)); if (Parenthesize) attrs.Add(new ParenthesizePropertyNameAttribute(true)); FlagsAttribute[] a = (FlagsAttribute[])PropertyType.GetCustomAttributes(typeof(FlagsAttribute), true); if (PropertyType != null && a.Length > 0) attrs.Add(new EditorAttribute(typeof(FlagsEditor), typeof(UITypeEditor))); if (IsPercentage) attrs.Add(new TypeConverterAttribute(typeof(OpacityConverter))); if (PropertyType == typeof(string)) attrs.Add(new TypeConverterAttribute(typeof(List2PropertyConverter))); if (DefaultValue == null) attrs.Add(new DefaultValueAttribute(DefaultValue)); return new CustomPropertyDescriptor(this, attrs.ToArray()); } #region == private adapter classes== public class CustomPropertyDescriptor : PropertyDescriptor { private CustomProperty _property; public CustomPropertyDescriptor(CustomProperty myProperty, Attribute[] attrs) : base(myProperty.DisplayName, attrs) { _property = myProperty; } public CustomProperty Property { get { return _property; } } public override Type ComponentType { get { return GetType(); } } public override object GetValue(object component) { return Property.Value; } public override bool IsReadOnly { get { return Property.IsReadOnly; } } public override Type PropertyType { get { return Property.PropertyType; } } public override string Description { get { return Property.Description; } } public override string Category { get { return Property.Category; } } public override string DisplayName { get { return Property.DisplayName; } } public override bool IsBrowsable { get { return false; } } public override bool CanResetValue(object component) { return (Property.DefaultValue != null); } public override void ResetValue(object component) { Property.Value = Property.DefaultValue; OnValueChanged(component, EventArgs.Empty); } public override void SetValue(object component, object value) { Property.Value = value; OnValueChanged(component, EventArgs.Empty); } public override bool ShouldSerializeValue(object component) { object oValue = Property.Value; if ((Property.DefaultValue != null) && (oValue != null)) return !oValue.Equals(Property.DefaultValue); return false; } } internal class List2PropertyConverter : StringConverter { public override bool GetStandardValuesSupported(ITypeDescriptorContext context) { CustomPropertyDescriptor cp = context.PropertyDescriptor as CustomPropertyDescriptor; return (cp != null && cp.Property.StandardValues != null); } public override bool GetStandardValuesExclusive(ITypeDescriptorContext context) { CustomPropertyDescriptor cp = context.PropertyDescriptor as CustomPropertyDescriptor; return (cp != null && cp.Property.OnlyStandardValues); } public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context) { CustomPropertyDescriptor cp = context.PropertyDescriptor as CustomPropertyDescriptor; return new StandardValuesCollection(cp.Property.StandardValues); } } internal class FlagsEditor : UITypeEditor { class ComboItem { public ComboItem(string displayName, ulong value, string tooltip) { DisplayName = displayName; Value = value; Tooltip = tooltip; } public ulong Value; public string Tooltip; public string DisplayName; public override string ToString() { return DisplayName; } } private System.Windows.Forms.Design.IWindowsFormsEditorService _edSvc = null; private CheckedListBox _clb; private ToolTip _tooltipControl; private bool _handleLostfocus = false; public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value) { if (context == null || context.Instance == null || provider == null) return value; _edSvc = (System.Windows.Forms.Design.IWindowsFormsEditorService)provider.GetService(typeof(System.Windows.Forms.Design.IWindowsFormsEditorService)); if (_edSvc == null) return value; // Create a CheckedListBox and populate it with all the enum values _clb = new CheckedListBox(); _clb.Sorted = false; _clb.BorderStyle = BorderStyle.FixedSingle; _clb.CheckOnClick = true; _clb.MouseDown += OnMouseDown; _clb.MouseMove += OnMouseMoved; _clb.ItemCheck += OnItemCheck; _tooltipControl = new ToolTip(); _tooltipControl.ShowAlways = true; ulong intEdited = (ulong)Convert.ChangeType(value ?? 0, typeof(ulong)); foreach (string name in Enum.GetNames(context.PropertyDescriptor.PropertyType)) { object enumVal = Enum.Parse(context.PropertyDescriptor.PropertyType, name); ulong enumNumValue = (ulong)Convert.ChangeType(enumVal, typeof(ulong)); System.Reflection.FieldInfo fi = context.PropertyDescriptor.PropertyType.GetField(name); DescriptionAttribute[] attrs = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false); string tooltip = attrs.Length > 0 ? attrs[0].Description : string.Empty; CheckState cs = CheckState.Indeterminate; if ((intEdited & enumNumValue) == 0) cs = CheckState.Unchecked; else if ((intEdited & enumNumValue) == enumNumValue) cs = CheckState.Checked; _clb.Items.Add(new ComboItem(name, enumNumValue, tooltip), cs); } if (_clb.Items.Count > 4) { _clb.Height = _clb.ItemHeight * Math.Min(_clb.Items.Count + 1, 10); } // Show our CheckedListbox as a DropDownControl. // This methods returns only when the dropdowncontrol is closed _edSvc.DropDownControl(_clb); // Get the sum of all checked flags ulong result = GetResult(-1, CheckState.Indeterminate); // return the right enum value corresponding to the result return Enum.ToObject(context.PropertyDescriptor.PropertyType, result); } ulong GetResult(int nv, CheckState cs) { ulong result = 0; for (int i = 0; i < _clb.Items.Count; ++i) { CheckState c = (i == nv) ? cs : _clb.GetItemCheckState(i); if (c == CheckState.Checked) result |= ((ComboItem)_clb.Items[i]).Value; } return result; } private volatile int _loopCheck = 0; private void OnItemCheck(object sender, ItemCheckEventArgs e) { if (_loopCheck++ == 0) { ulong result = GetResult(e.Index, e.NewValue); for (int i = 0; i < _clb.Items.Count; ++i) { ulong v = ((ComboItem)_clb.Items[i]).Value; CheckState cs = CheckState.Indeterminate; if ((result & v) == 0) cs = CheckState.Unchecked; else if ((result & v) == v) cs = CheckState.Checked; _clb.SetItemCheckState(i, cs); } } _loopCheck--; } public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context) { return UITypeEditorEditStyle.DropDown; } private void OnMouseDown(object sender, MouseEventArgs e) { if (!_handleLostfocus && _clb.ClientRectangle.Contains(_clb.PointToClient(new Point(e.X, e.Y)))) { _clb.LostFocus += ValueChanged; _handleLostfocus = true; } } private void OnMouseMoved(object sender, MouseEventArgs e) { int index = _clb.IndexFromPoint(e.X, e.Y); if (index >= 0) _tooltipControl.SetToolTip(_clb, ((ComboItem)_clb.Items[index]).Tooltip); } private void ValueChanged(object sender, EventArgs e) { if (_edSvc != null) _edSvc.CloseDropDown(); } } #endregion } #endregion ?> </sub> </xsharper>