gui-param.xsh

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>