TextEditorOverlay.cs
上传用户:hbhltzc
上传日期:2022-06-04
资源大小:1925k
文件大小:27k
源码类别:

xml/soap/webservice

开发平台:

Visual C++

  1. using System;
  2. using System.Xml;
  3. using System.Windows.Forms;
  4. using System.Drawing;
  5. using System.Drawing.Drawing2D;
  6. using System.Diagnostics;
  7. using System.ComponentModel;
  8. using System.Text;
  9. using System.Runtime.InteropServices;
  10. using System.Xml.Schema;
  11. namespace XmlNotepad {
  12.     
  13.     public class TextEditorEventArgs : EventArgs {
  14.         string text;
  15.         bool cancelled;
  16.         public string Text {
  17.             get { return text; }
  18.         }
  19.         public bool Cancelled {
  20.             get { return cancelled; }
  21.             set { this.cancelled = value; }
  22.         }
  23.         public TextEditorEventArgs(string text, bool cancelled) {
  24.             this.text = text;
  25.             this.cancelled = cancelled;
  26.         }
  27.     }
  28.     public class TextEditorLayoutEventArgs : EventArgs {
  29.         string text;
  30.         Rectangle preferredBounds;
  31.         Rectangle maxBounds;
  32.         
  33.         public string Text {
  34.             get { return text; }
  35.         }
  36.         
  37.         public Rectangle PreferredBounds {
  38.             get { return preferredBounds; }
  39.             set { preferredBounds = value; }
  40.         }
  41.         public Rectangle MaxBounds {
  42.             get { return maxBounds; }
  43.             set { maxBounds = value; }
  44.         }
  45.         public TextEditorLayoutEventArgs(string text) {
  46.             this.text = text; 
  47.         }
  48.     }
  49.     public enum EditMode { Name, Value };
  50.     public class TextEditorOverlay : IDisposable {
  51.         private TextBox textEditor;
  52.         private Control parent;
  53.         private bool autoSize;
  54.         private CompletionSet cset;
  55.         private IXmlEditor editor;
  56.         private Control currentEditor;
  57.         private XmlSchemaType schemaType;
  58.         private ISite site;
  59.         public event EventHandler<TextEditorEventArgs> CommitEdit;
  60.         public event EventHandler<TextEditorLayoutEventArgs> LayoutEditor;
  61.         public TextEditorOverlay(Control parent) {
  62.             this.parent = parent;
  63.             this.textEditor = new TextBox();
  64.             string name = parent.Name + "Editor"; 
  65.             this.textEditor.Name = name;
  66.             this.textEditor.AccessibleName = name;
  67.             this.textEditor.Visible = false;
  68.             this.textEditor.BorderStyle = BorderStyle.None;
  69.             this.textEditor.BackColor = Color.LightSteelBlue;
  70.             this.textEditor.AutoSize = false;
  71.             this.textEditor.Multiline = true; // this fixes layout problems in single line case also.
  72.             this.textEditor.Margin = new Padding(1, 0, 0, 0);
  73.             this.textEditor.HideSelection = false;
  74.             parent.Controls.Add(this.textEditor);
  75.             this.textEditor.KeyDown += new KeyEventHandler(editor_KeyDown);
  76.             this.textEditor.LostFocus += new EventHandler(editor_LostFocus);
  77.             this.textEditor.GotFocus += new EventHandler(editor_GotFocus);
  78.             this.textEditor.TextChanged += new EventHandler(editor_TextChanged);
  79.             this.currentEditor = this.textEditor;
  80.             this.cset = new CompletionSet(this.textEditor);
  81.             this.cset.KeyDown += new KeyEventHandler(editor_KeyDown);
  82.             this.cset.DoubleClick += new EventHandler(cset_DoubleClick);
  83.         }
  84.         ~TextEditorOverlay() {
  85.             Dispose(false);
  86.         }
  87.         public ISite Site {
  88.             get { return site; }
  89.             set { site = this.cset.Site = value; }
  90.         }
  91.         public bool AutoSize {
  92.             get { return this.autoSize; }
  93.             set { this.autoSize = value; }
  94.         }
  95.         public bool MultiLine {
  96.             get { 
  97.                 return this.textEditor.Multiline;
  98.             }
  99.             set {
  100.                 this.textEditor.ScrollBars = value ? ScrollBars.Vertical : ScrollBars.None;
  101.                 this.textEditor.Multiline = value;  
  102.             }
  103.         }
  104.         public bool IsEditing { get { return this.currentEditor.Visible; } }
  105.         public Rectangle Bounds {
  106.             get { return currentEditor.RectangleToScreen(currentEditor.ClientRectangle); }
  107.         }
  108.         internal CompletionSet CompletionSet {
  109.             get { return this.cset; }
  110.         }
  111.         public void PerformLayout() {
  112.             if (this.LayoutEditor != null) {
  113.                 TextEditorLayoutEventArgs args = new TextEditorLayoutEventArgs(this.currentEditor.Text);
  114.                 LayoutEditor(this, args);
  115.                 SetEditorBounds(args);
  116.             }
  117.         }        
  118.         public void BeginEdit(string text, IIntellisenseProvider provider, EditMode mode, Color color, bool focus) {
  119.             IXmlBuilder builder = null;
  120.             IIntellisenseList list = null;
  121.             if (focus) {
  122.                 switch (mode) {
  123.                     case EditMode.Value:
  124.                         builder = provider.Builder;
  125.                         this.cset.Builder = builder;
  126.                         this.editor = provider.Editor;
  127.                         if (this.editor != null) {
  128.                             this.editor.Site = this.site;
  129.                         }
  130.                         list = provider.GetExpectedValues();
  131.                         break;
  132.                     case EditMode.Name:
  133.                         list = provider.GetExpectedNames();
  134.                         break;
  135.                 }
  136.             }
  137.             this.schemaType = provider.GetSchemaType();
  138.             if (this.editor != null) {
  139.                 this.currentEditor = this.editor.Editor;
  140.                 parent.Controls.Add(this.currentEditor);
  141.                 this.editor.SchemaType = this.schemaType;    
  142.                 this.currentEditor.KeyDown += new KeyEventHandler(editor_KeyDown);
  143.                 this.editor.XmlValue = text;
  144.             } else {
  145.                 this.currentEditor = this.textEditor;
  146.                 this.currentEditor.Text = text;
  147.             }
  148.             this.currentEditor.ForeColor = color;            
  149.             PerformLayout();
  150.             this.currentEditor.Visible = true;
  151.             if (focus) {
  152.                 this.currentEditor.Focus();
  153.                 if (this.currentEditor == this.textEditor) {
  154.                     this.textEditor.SelectAll();
  155.                 }
  156.                 // see if this node needs a dropdown.
  157.                 if (builder != null || (list != null && list.Count > 0)) {
  158.                     cset.BeginEdit(list, this.schemaType);
  159.                 } 
  160.             }
  161.         }
  162.         public void SelectEnd() {
  163.             if (this.currentEditor == this.textEditor && this.currentEditor.Visible) {
  164.                 this.textEditor.SelectionStart = this.textEditor.Text.Length;
  165.             }
  166.         }
  167.         public void Select(int index, int length) {
  168.             if (this.currentEditor == this.textEditor && this.currentEditor.Visible) {
  169.                 this.textEditor.SelectionStart = index;
  170.                 this.textEditor.SelectionLength = length;
  171.             }
  172.         }
  173.         public int SelectionStart { get { return this.textEditor.SelectionStart; } }
  174.         public int SelectionLength { get { return this.textEditor.SelectionLength; } }
  175.         public bool Replace(int index, int length, string replacement) {
  176.             if (this.currentEditor == this.textEditor && this.currentEditor.Visible) {
  177.                 int end = index + length;
  178.                 string s = this.currentEditor.Text;
  179.                 string head = (index > 0) ? s.Substring(0, index) : "";
  180.                 string tail = (end < s.Length) ? s.Substring(end) : "";
  181.                 this.currentEditor.Text = head + replacement + tail;
  182.                 return true;
  183.             }
  184.             return false;
  185.         }
  186.         bool cancel;
  187.         Timer at;
  188.         public void StartEndEdit(bool cancel) {
  189.             this.cancel = cancel;
  190.             if (at == null) {
  191.                 at = new Timer();
  192.                 at.Interval = 10;
  193.                 at.Tick += new EventHandler(OnEndTick);
  194.             }
  195.             at.Start();
  196.         }
  197.         void OnEndTick(object sender, EventArgs e) {
  198.             at.Stop();
  199.             EndEdit(cancel);
  200.         }
  201.         bool ending;
  202.         public bool EndEdit(bool cancel) {
  203.             if (ending) return false; // don't let it be re-entrant!
  204.             ending = true;
  205.                             
  206.             cset.EndEdit(cancel);
  207.             try {
  208.                 if (this.currentEditor.Visible) {
  209.                     if (this.CommitEdit != null) {
  210.                         string value = (this.editor != null) ? this.editor.XmlValue : this.currentEditor.Text;
  211.                         TextEditorEventArgs args = new TextEditorEventArgs(value, cancel);
  212.                         CommitEdit(this, args);
  213.                         if (args.Cancelled && !cancel)
  214.                             return false;
  215.                     }
  216.                     HideEdit();
  217.                 }
  218.             } catch (Exception e) {
  219.                 MessageBox.Show(e.Message, SR.EditErrorCaption, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
  220.             } finally {                
  221.                 ending = false;
  222.             }
  223.             return true;
  224.         }
  225.         const ushort WM_CHAR = 0x0102;
  226.         Keys lastKey;
  227.         private void editor_KeyDown(object sender, KeyEventArgs e) {
  228.             CurrentEvent.Event = e;
  229.             this.lastKey = e.KeyCode;
  230.             switch (e.KeyCode) {
  231.                 case Keys.Enter:
  232.                     if (e.Modifiers == 0) {
  233.                         e.SuppressKeyPress = true;
  234.                         e.Handled = true;
  235.                         StartEndEdit(false); // must be async!
  236.                     }
  237.                     break;
  238.                 case Keys.Escape:
  239.                     e.Handled = true;
  240.                     if (this.cset.ToolTipVisible) {
  241.                         this.cset.HideToolTip();
  242.                     } else if (this.cset.Visible) {
  243.                         this.cset.EndEdit(true);
  244.                         this.currentEditor.Focus();
  245.                     } else {
  246.                         StartEndEdit(true);
  247.                     }
  248.                     break;
  249.                 default:
  250.                     IEditableView v = this.parent as IEditableView;
  251.                     if (v != null){
  252.                         bool old = e.SuppressKeyPress;
  253.                         e.SuppressKeyPress = true;
  254.                         try {
  255.                             e.Handled = false;
  256.                             v.BubbleKeyDown(e);
  257.                         } finally {
  258.                             e.SuppressKeyPress = old;
  259.                         }
  260.                     }
  261.                     break;
  262.             }
  263.         }
  264.         void cset_DoubleClick(object sender, EventArgs e) {
  265.             EndEdit(false);
  266.         }
  267.         private void editor_LostFocus(object sender, EventArgs e){
  268.             if (!parent.ContainsFocus && !cset.ContainsFocus){
  269.                 EndEdit(false);
  270.             }            
  271.         }
  272.         private void editor_GotFocus(object sender, EventArgs e) {
  273.             return;
  274.         }
  275.         private void editor_TextChanged(object sender, EventArgs e) {
  276.             PerformLayout();
  277.         }
  278.         void AdjustBounds(string text, Graphics g, TextEditorLayoutEventArgs args) {
  279.             Rectangle r = args.PreferredBounds;
  280.             if (AutoSize) {
  281.                 if (string.IsNullOrEmpty(text))
  282.                     text = "W"; // double the size if it was empty to begin with.
  283.                 text += "W"; // leave room to grow by one more char
  284.             }
  285.             if (text.EndsWith("n")) text += "."; // cause MeasureString to include space for newlines.
  286.             SizeF size = SizeF.Empty;
  287.             int maxHeight = (this.parent.Height * 2) / 3;
  288.             try {
  289.                 if (text.Length >= 10000) {
  290.                     // MeasureString gets too slow after a certain size.
  291.                     text = text.Substring(0, 10000);
  292.                 }
  293.                 size = g.MeasureString(text, this.parent.Font, this.parent.Width, StringFormat.GenericDefault);
  294.             } catch (Exception) {
  295.                 // string might be too long to measure!
  296.                 size = new SizeF(r.Width, maxHeight);
  297.             }
  298.             int h = (int)Math.Max(r.Height, Math.Ceiling(size.Height));
  299.             if (h > r.Height ) {
  300.                 // when multiline, add padding so that it aligns correctly.
  301.                 h += this.parent.Font.Height / 2;
  302.             }            
  303.             r.Height = Math.Min(maxHeight, h); // no more than 2/3rd of the window.
  304.             if (AutoSize) {
  305.                 r.Width = Math.Max(r.Width, (int)size.Width + 2);
  306.             }
  307.             if (r.Right > args.MaxBounds.Right)
  308.                 r.Width = args.MaxBounds.Right - r.Left;
  309.             args.PreferredBounds = r;
  310.         }
  311.         void SetEditorBounds(TextEditorLayoutEventArgs args) {
  312.             string text = this.currentEditor.Text;
  313.             Graphics g = this.parent.CreateGraphics();            
  314.             using (g) {
  315.                 AdjustBounds(text, g, args);
  316.             }
  317.             Rectangle r = args.PreferredBounds;
  318.             if (r.Bottom > this.parent.Height) {
  319.                 // todo: scroll the view so we don't have to pop-up backwards, but this is tricky because
  320.                 // we may need to scroll more than the XmlTreeView scrollbar maximum in the case where the 
  321.                 // last node has a lot of text...
  322.                 r.Offset(new Point(0, this.parent.Height - r.Bottom));
  323.             }
  324.             int maxHeight = (this.parent.Height * 2) / 3;
  325.             if (r.Height < maxHeight) {
  326.                 int h = this.currentEditor.PreferredSize.Height;
  327.                 if (r.Height < h) {
  328.                     r.Y -= (h - r.Height) / 2;
  329.                     if (r.Y < 0) r.Y = 0;
  330.                     r.Height = h;
  331.                 }
  332.             }
  333.             Rectangle or = this.currentEditor.Bounds;
  334.             if (or.Left != r.Left || or.Right != r.Right || or.Top != r.Top || or.Bottom != r.Bottom) {
  335.                 this.currentEditor.Bounds = r;
  336.             }
  337.         }
  338.         void HideEdit() {
  339.             if (this.currentEditor.Visible) {
  340.                 bool wasFocused = this.currentEditor.ContainsFocus || this.cset.ContainsFocus;
  341.                 this.currentEditor.Visible = false;
  342.                 this.cset.Visible = false;
  343.                 if (this.editor != null && this.currentEditor != this.textEditor) {
  344.                     parent.Controls.Remove(this.currentEditor);
  345.                     this.currentEditor.KeyDown -= new KeyEventHandler(editor_KeyDown);
  346.                     this.currentEditor.LostFocus -= new EventHandler(editor_LostFocus);
  347.                     this.currentEditor = this.textEditor;
  348.                     this.editor = null;
  349.                 }
  350.                 if (wasFocused) this.parent.Focus();
  351.             }
  352.         }
  353.         public void Dispose() {
  354.             Dispose(true);
  355.             GC.SuppressFinalize(this);
  356.         }
  357.         protected virtual void Dispose(bool disposing) {
  358.             if (this.textEditor != null) {
  359.                 this.parent.Controls.Remove(this.textEditor);
  360.                 this.textEditor.Dispose();
  361.                 this.textEditor = null;
  362.                 this.textEditor = null;
  363.             }
  364.             if (this.cset != null) {
  365.                 this.cset.Builder = null;
  366.                 this.cset.Dispose();
  367.                 this.cset = null;
  368.             }
  369.             // do NOT dispose this guy, it's owned by someone else.
  370.             this.editor = null;
  371.         }
  372.     }
  373.     // This class is used to display a set of names and icons, track user edits
  374.     // in associated text box and automatically select the item that best matches
  375.     // what the user is doing.
  376.     class CompletionSet : Control {
  377.         bool parented;
  378.         TextBox editor;
  379.         ListBox listBox;
  380.         IXmlBuilder builder;
  381.         Button button;
  382.         XmlSchemaType type;
  383.         IIntellisenseList list;
  384.         IntelliTip tip;
  385.         public CompletionSet(TextBox editor) {
  386.             this.SetStyle(ControlStyles.Selectable, true);
  387.             this.listBox = new ListBox();
  388.             this.listBox.Name = "CompletionList";
  389.             this.listBox.AccessibleName = "CompletionList";
  390.             this.listBox.BorderStyle = BorderStyle.Fixed3D;
  391.             this.listBox.KeyDown += new KeyEventHandler(listBox_KeyDown);
  392.             this.listBox.DoubleClick += new EventHandler(listBox_DoubleClick);
  393.             this.listBox.AutoSize = true;
  394.             this.listBox.SelectedIndexChanged += new EventHandler(listBox_SelectedIndexChanged);
  395.             this.editor = editor;
  396.             this.editor.TextChanged += new EventHandler(editor_TextChanged);
  397.             this.editor.KeyDown += new KeyEventHandler(editor_KeyDown);
  398.             this.button = new Button();
  399.             this.button.Name = "BuilderButton";
  400.             this.button.AccessibleName = "BuilderButton";
  401.             this.button.Visible = false;
  402.             this.button.Click += new EventHandler(button_Click);
  403.             this.button.AutoSize = true;
  404.             this.button.KeyDown += new KeyEventHandler(editor_KeyDown);
  405.             this.Visible = false;
  406.             this.tip = new IntelliTip(editor);
  407.             this.tip.AddWatch(this.listBox);
  408.             this.tip.ShowToolTip += new IntelliTipEventHandler(OnShowToolTip);
  409.             this.Controls.Add(this.listBox);
  410.             this.Controls.Add(this.button);
  411.             this.AccessibleName = "CompletionSet";
  412.         }
  413.         void listBox_SelectedIndexChanged(object sender, EventArgs e) {
  414.             this.HideToolTip();
  415.             this.tip.OnShowToolTip();
  416.         }
  417.         public bool ToolTipVisible {
  418.             get { return this.tip.Visible;  }
  419.         }
  420.         public void HideToolTip() {
  421.             this.tip.Hide(); 
  422.         }
  423.         void OnShowToolTip(object sender, IntelliTipEventArgs args) {
  424.             if (list != null) {
  425.                 int i = this.listBox.SelectedIndex;
  426.                 if (args.Type == TipRequestType.Hover) {
  427.                     Point pt = args.Location;
  428.                     for (int j = 0, n = this.listBox.Items.Count; j < n; j++) {
  429.                         Rectangle r = this.listBox.GetItemRectangle(j);
  430.                         if (r.Contains(pt)) {
  431.                             i = j;
  432.                             break;
  433.                         }
  434.                     }
  435.                 }
  436.                 if (i >= 0 && i < list.Count) {
  437.                     string t = list.GetTooltip(i);
  438.                     if (!string.IsNullOrEmpty(t)) {
  439.                         Rectangle r = this.listBox.GetItemRectangle(i);
  440.                         Point p = new Point(r.Right, r.Top);
  441.                         p = this.listBox.PointToScreen(p);
  442.                         Screen screen = Screen.FromPoint(p);
  443.                         using (Graphics g = this.CreateGraphics()) {
  444.                             SizeF s = g.MeasureString(t, SystemFonts.MenuFont);
  445.                             if (p.X + s.Width > screen.Bounds.Right) {
  446.                                 p.X = screen.Bounds.Right - (int)s.Width;
  447.                                 if (p.X < 0) p.X = 0;
  448.                                 p.Y += this.listBox.ItemHeight;
  449.                             }
  450.                         }
  451.                         args.Location = args.Focus.PointToClient(p);
  452.                         args.ToolTip = t;
  453.                     }
  454.                 }
  455.             }
  456.         }
  457.         void button_Click(object sender, EventArgs e) {
  458.             if (builder != null) {
  459.                 string result = this.editor.Text;
  460.                 if (builder.EditValue(this, type, result, out result)) {
  461.                     this.editor.Text = result;
  462.                 }
  463.             }
  464.         }
  465.         void listBox_DoubleClick(object sender, EventArgs e) {
  466.             this.OnDoubleClick(e);
  467.         }
  468.         void listBox_KeyDown(object sender, KeyEventArgs e) {
  469.             CurrentEvent.Event = e;
  470.             this.OnKeyDown(e);
  471.         }
  472.         public IXmlBuilder Builder {
  473.             get { return this.builder; }
  474.             set { 
  475.                 this.builder = value;
  476.                 if (value != null) {
  477.                     value.Site = this.Site;
  478.                 }
  479.             }
  480.         }        
  481.         void editor_KeyDown(object sender, KeyEventArgs e) {
  482.             CurrentEvent.Event = e;
  483.             if (e.Handled) return; // some other listener already handled the event.
  484.             int i = this.listBox.SelectedIndex;
  485.             switch (e.KeyCode) {
  486.                 case Keys.Down:
  487.                     i++;
  488.                     break;
  489.                 case Keys.Up:
  490.                     i--;
  491.                     break;
  492.                 case Keys.Home:
  493.                     i = 0;
  494.                     break;
  495.                 case Keys.End:
  496.                     i = this.listBox.Items.Count - 1;
  497.                     break;
  498.                 case Keys.PageUp:
  499.                     i -= this.Height / this.listBox.ItemHeight;
  500.                     break;
  501.                 case Keys.PageDown:
  502.                     i += this.Height / this.listBox.ItemHeight;
  503.                     break;
  504.                 case Keys.Enter:
  505.                     OnKeyDown(e);
  506.                     break;                
  507.             }
  508.             if (i != this.listBox.SelectedIndex && this.listBox.Items.Count > 0) {
  509.                 if (i > this.listBox.Items.Count - 1) i = this.listBox.Items.Count - 1;
  510.                 if (i < 0) i = 0;
  511.                 this.listBox.SelectedIndex = i;
  512.             }
  513.         }
  514.         const uint WS_POPUP =  0x80000000;
  515.         const uint WS_EX_TOPMOST = 0x00000008;
  516.         protected override CreateParams CreateParams {
  517.             get {
  518.                 CreateParams cp = base.CreateParams;
  519.                 cp.Style |= ForceCast(WS_POPUP);
  520.                 cp.ExStyle |= ForceCast(WS_EX_TOPMOST);
  521.                 return cp;
  522.             }
  523.         }
  524.         static int ForceCast(uint i) {
  525.             unchecked {
  526.                 return (int)i;
  527.             }
  528.         }
  529.         
  530.         void OnWindowMoved(object sender, EventArgs e) {
  531.             if (this.Visible) {
  532.                 PositionPopup();
  533.             }
  534.         }
  535.         void editor_TextChanged(object sender, EventArgs e) {
  536.             // find best match & scroll it into view and select it
  537.             string s = this.editor.Text;
  538.             if (string.IsNullOrEmpty(s)) {
  539.                 this.listBox.SelectedItem = null;
  540.             } else {
  541.                 int i = this.listBox.FindString(s);
  542.                 if (i >= 0) {
  543.                     this.listBox.SelectedIndex = i;
  544.                     return;
  545.                 } else {
  546.                     // Find case-insensitive.
  547.                     i = 0;
  548.                     foreach (string value in this.listBox.Items) {
  549.                         if (value.StartsWith(s, StringComparison.CurrentCultureIgnoreCase)) {
  550.                             this.listBox.SelectedIndex = i;
  551.                             return;
  552.                         }
  553.                         i++;
  554.                     }
  555.                 }
  556.                 this.listBox.SelectedItem = null;
  557.             }
  558.         }
  559.         public void BeginEdit(IIntellisenseList list, XmlSchemaType type) {
  560.             this.type = type;
  561.             this.listBox.Font = this.button.Font = this.editor.Font;
  562.             this.listBox.ForeColor = this.editor.ForeColor;
  563.             this.list = list;
  564.             // populate the list and display it under (or above) the editor.
  565.             this.listBox.Items.Clear();
  566.             if (list != null) {
  567.                 for (int i = 0, n = list.Count; i<n; i++) {
  568.                     string s = list.GetValue(i);
  569.                     int j = this.listBox.Items.Add(s);
  570.                     if (s == this.editor.Text) {
  571.                         this.listBox.SelectedIndex = j;
  572.                     }
  573.                 }
  574.                 
  575.             }
  576.             if (!parented) {
  577.                 SetParent(this.Handle, IntPtr.Zero); // popup on desktop.
  578.                 parented = true;
  579.                 Control p = this.editor;
  580.                 while (p.Parent != null) {
  581.                     p = p.Parent;
  582.                 }
  583.                 p.Move += new EventHandler(OnWindowMoved);            
  584.             }
  585.             PositionPopup();
  586.             this.Visible = true;
  587.             this.editor.Focus();
  588.         }
  589.         public void EndEdit(bool cancel) {
  590.             this.HideToolTip();
  591.             if (!cancel && this.Visible && this.listBox.SelectedItem != null) {
  592.                 this.editor.Text = (string)this.listBox.SelectedItem;
  593.                 this.editor.Focus();
  594.             }
  595.             this.Visible = false;
  596.         }
  597.         void PositionPopup() {
  598.             PerformLayout();
  599.             Rectangle r = this.editor.Parent.RectangleToScreen(this.editor.Bounds);
  600.             Screen s = Screen.FromRectangle(r);
  601.             Point p = new Point(r.Left, r.Bottom);
  602.             if (r.Bottom + this.Height > s.WorkingArea.Height) {
  603.                 // pop up instead of down!
  604.                 p = new Point(r.Left, r.Top - this.Height);
  605.             }
  606.             this.Location = p;
  607.         }
  608.         protected override void OnLayout(LayoutEventArgs levent) {
  609.             Rectangle r = this.editor.Parent.RectangleToScreen(this.editor.Bounds);
  610.             Screen s = Screen.FromRectangle(r);
  611.             Size max = new Size(s.WorkingArea.Width / 3, (this.listBox.ItemHeight * 10) + 4);
  612.             this.listBox.MaximumSize = max;
  613.             Size size = new Size(0, 0);
  614.             bool listVisible = (this.listBox.Items.Count > 0);
  615.             if (!listVisible) {
  616.                 this.listBox.Size = size;
  617.                 this.listBox.Visible = false;
  618.             } else {
  619.                 this.listBox.Visible = true;
  620.                 size = this.listBox.PreferredSize;
  621.                 if (size.Height > max.Height) {
  622.                     size.Height = max.Height;
  623.                 }
  624.                 this.listBox.Size = size;
  625.             }
  626.             size = this.listBox.Size; // just in case listBox snapped to a different bounds.
  627.             if (this.builder != null) {
  628.                 this.button.Text = this.builder.Caption;
  629.                 this.button.Visible = true;
  630.                 this.button.Size = new Size(10, 10);
  631.                 this.button.Size = this.button.PreferredSize;
  632.                 if (size.Width < this.button.Width) {
  633.                     size.Width = this.button.Width;
  634.                     if (listVisible) this.listBox.Width = size.Width;
  635.                 } 
  636.                 this.button.Width = size.Width;
  637.                 this.listBox.Location = new Point(0, this.button.Height);                
  638.                 size.Height += this.button.Height;
  639.             } else {
  640.                 this.listBox.Location = new Point(0, 0);
  641.                 this.button.Visible = false;
  642.             }
  643.             this.Size = size;            
  644.         }
  645.         [DllImport("User32.dll", EntryPoint = "SetParent")]
  646.         internal extern static IntPtr SetParent(IntPtr hwndChild, IntPtr hwndParent);
  647.     }
  648. }