CustomComboBox.cs
上传用户:cpw91211
上传日期:2021-01-18
资源大小:46k
文件大小:20k
源码类别:

组合框控件

开发平台:

C#

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Text;
  4. using System.Windows.Forms;
  5. using System.ComponentModel;
  6. using System.Drawing;
  7. using System.Security.Permissions;
  8. namespace CustomComboBox
  9. {
  10.     /// <summary>
  11.     /// <c>CustomComboBox</c> is an extension of <c>ComboBox</c> which provides drop-down customization.
  12.     /// </summary>
  13.     [SecurityPermissionAttribute(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
  14.     public class CustomComboBox : ComboBox
  15.     {
  16.         #region Construction and destruction
  17.         public CustomComboBox()
  18.             : base()
  19.         {
  20.             m_sizeCombo = new Size(base.DropDownWidth, base.DropDownHeight);
  21.             m_dropDown.Closing += new ToolStripDropDownClosingEventHandler(m_dropDown_Closing);
  22.         }
  23.         void m_dropDown_Closing(object sender, ToolStripDropDownClosingEventArgs e)
  24.         {
  25.             m_lastHideTime = DateTime.Now;
  26.         }
  27.         public CustomComboBox(Control dropControl)
  28.             : this()
  29.         {
  30.             DropDownControl = dropControl;
  31.         }
  32.         #endregion
  33.         #region ComboBox overrides
  34.         protected override void Dispose(bool disposing)
  35.         {
  36.             if (disposing)
  37.             {
  38.                 if (m_dropDown != null)
  39.                 {
  40.                     m_dropDown.Dispose();
  41.                     m_dropDown = null;
  42.                 }
  43.                 if (m_dropDownHost != null)
  44.                 {
  45.                     m_dropDownHost.Dispose();
  46.                     m_dropDownHost = null;
  47.                 }
  48.                 if(m_timerAutoFocus != null)
  49.                 {
  50.                     m_timerAutoFocus.Dispose();
  51.                     m_timerAutoFocus = null;
  52.                 }
  53.             }
  54.             base.Dispose(disposing);
  55.         }
  56.         #endregion
  57.         #region Event handlers
  58.         private void timerAutoFocus_Tick(object sender, EventArgs e)
  59.         {
  60.             if (m_dropDownHost.IsOnDropDown && !DropDownControl.Focused)
  61.             {
  62.                 DropDownControl.Focus();
  63.                 m_timerAutoFocus.Enabled = false;
  64.             }
  65.         }
  66.         private void m_dropDown_LostFocus(object sender, EventArgs e)
  67.         {
  68.             m_lastHideTime = DateTime.Now;
  69.         }
  70.         #endregion
  71.         #region Methods
  72.         /// <summary>
  73.         /// Prepare custom container to hold drop-down control.
  74.         /// </summary>
  75.         /// <param name="control">Control to contain.</param>
  76.         /// <returns>Specialized container.</returns>
  77.         protected virtual Control PrepareDropDown(Control control)
  78.         {
  79.             if (AllowResizeDropDown)
  80.             {
  81.                 // Encapsulate contorl within a resizable container.
  82.                 return new ResizableDropDownContainer(control);
  83.             }
  84.             else
  85.             {
  86.                 // By default simply return the drop down control.
  87.                 return control;
  88.             }
  89.         }
  90.         /// <summary>
  91.         /// Displays drop-down area of combo box, if not already shown.
  92.         /// </summary>
  93.         public virtual void ShowDropDown()
  94.         {
  95.             if (DropDownContainer != null && m_dropDownHost != null && !IsDroppedDown)
  96.             {
  97.                 // Restore original container size.
  98.                 AutoSizeDropDown();
  99.                 // Display drop-down control.
  100.                 m_dropDown.Show(this, 0, this.Height);
  101.                 m_bDroppedDown = true;
  102.                 // Initialize automatic focus timer?
  103.                 if (m_timerAutoFocus == null)
  104.                 {
  105.                     m_timerAutoFocus = new Timer();
  106.                     m_timerAutoFocus.Interval = 10;
  107.                     m_timerAutoFocus.Tick += new EventHandler(timerAutoFocus_Tick);
  108.                 }
  109.                 // Enable the timer!
  110.                 m_timerAutoFocus.Enabled = true;
  111.                 m_sShowTime = DateTime.Now;
  112.             }
  113.         }
  114.         /// <summary>
  115.         /// Hides drop-down area of combo box, if shown.
  116.         /// </summary>
  117.         public virtual void HideDropDown()
  118.         {
  119.             if (DropDownContainer != null && m_dropDownHost != null && IsDroppedDown)
  120.             {
  121.                 // Hide drop-down control.
  122.                 m_dropDown.Hide();
  123.                 m_bDroppedDown = false;
  124.                 
  125.                 // Disable automatic focus timer.
  126.                 if (m_timerAutoFocus != null && m_timerAutoFocus.Enabled)
  127.                     m_timerAutoFocus.Enabled = false;
  128.             }
  129.         }
  130.         /// <summary>
  131.         /// Automatically resize drop-down from properties.
  132.         /// </summary>
  133.         protected void AutoSizeDropDown()
  134.         {
  135.             if (m_dropDownHost != null)
  136.             {
  137.                 m_dropDownHost.Margin = m_dropDownHost.Padding = new Padding(0);
  138.                 switch (DropDownSizeMode)
  139.                 {
  140.                     case SizeMode.UseComboSize:
  141.                         DropDownContainer.Size = new Size(Width, m_sizeCombo.Height);
  142.                         break;
  143.                     case SizeMode.UseControlSize:
  144.                         DropDownContainer.Size = new Size(m_sizeOriginal.Width, m_sizeOriginal.Height);
  145.                         break;
  146.                     case SizeMode.UseDropDownSize:
  147.                         DropDownContainer.Size = m_sizeCombo;
  148.                         break;
  149.                 }
  150.             }
  151.         }
  152.         /// <summary>
  153.         /// Assigns control to custom drop-down area of combo box.
  154.         /// </summary>
  155.         /// <param name="control">Control to be used as drop-down. Please note that this control must not be contained elsewhere.</param>
  156.         protected virtual void AssignControl(Control control)
  157.         {
  158.             // If specified control is different then...
  159.             if (control != DropDownControl)
  160.             {
  161.                 // Preserve original container size.
  162.                 m_sizeOriginal = control.Size;
  163.                 // Reference the user-specified drop down control.
  164.                 m_dropDownCtrl = control;
  165.                 UpdateDropDownHost(control);
  166.             }
  167.         }
  168.         /// <summary>
  169.         /// Updates drop-down control host.
  170.         /// </summary>
  171.         /// <param name="control">Control being used as drop-down.</param>
  172.         protected void UpdateDropDownHost(Control control)
  173.         {
  174.             // Clear previous control.
  175.             m_dropDown.Items.Clear();
  176.             Size prevControlSize = control.Size;
  177.             // A new drop-down container will be required to store the newly specified
  178.             // control. If no container is specified, this will be equal to the newly
  179.             // specified control!
  180.             if (DropDownContainer != null && DropDownContainer != DropDownControl)
  181.             {
  182.                 m_dropDownCnt.Dispose();
  183.                 m_dropDownCnt = null;
  184.             }
  185.             m_dropDownCnt = PrepareDropDown(control);
  186.             // A new drop-down host is required to accomodate the newly specified control.
  187.             if (m_dropDownHost != null)
  188.             {
  189.                 m_dropDownHost.Dispose();
  190.                 m_dropDownHost = null;
  191.             }
  192.             m_dropDownHost = new ToolStripControlHost(DropDownContainer);
  193.             m_dropDownHost.LostFocus += new EventHandler(m_dropDown_LostFocus);
  194.             m_dropDownHost.AutoSize = false;
  195.             m_dropDown.Items.Add(m_dropDownHost);
  196.             // Calculate required drop-down size.
  197.             IDropDownContainer dropDownCnt = DropDownContainer as IDropDownContainer;
  198.             if (dropDownCnt != null)
  199.                 m_sizeOriginal = dropDownCnt.CalculateContainerSize(prevControlSize);
  200.             else
  201.                 m_sizeOriginal = prevControlSize;
  202.             AutoSizeDropDown();
  203.         }
  204.         #endregion
  205.         #region Win32 message handlers
  206.         public const uint WM_COMMAND = 0x0111;
  207.         public const uint WM_USER = 0x0400;
  208.         public const uint WM_REFLECT = WM_USER + 0x1C00;
  209.         public const uint WM_LBUTTONDOWN = 0x0201;
  210.         public const uint CBN_DROPDOWN = 7;
  211.         public const uint CBN_CLOSEUP = 8;
  212.         
  213.         public static uint HIWORD(int n)
  214.         {
  215.             return (uint)(n >> 16) & 0xffff;
  216.         }
  217.         public override bool PreProcessMessage(ref Message m)
  218.         {
  219.             if (m.Msg == (WM_REFLECT + WM_COMMAND))
  220.             {
  221.                 if (HIWORD((int)m.WParam) == CBN_DROPDOWN)
  222.                     return false;
  223.             }
  224.             return base.PreProcessMessage(ref m);
  225.         }
  226.         private static DateTime m_sShowTime = DateTime.Now;
  227.         private void AutoDropDown()
  228.         {
  229.             if (m_dropDown != null && m_dropDown.Visible)
  230.                 HideDropDown();
  231.             else if ((DateTime.Now - m_lastHideTime).Milliseconds > 50)
  232.                 ShowDropDown();
  233.         }
  234.         protected override void WndProc(ref Message m)
  235.         {
  236.             if (m.Msg == WM_LBUTTONDOWN)
  237.             {
  238.                 AutoDropDown();
  239.                 return;
  240.             }
  241.             if (m.Msg == (WM_REFLECT + WM_COMMAND))
  242.             {
  243.                 switch (HIWORD((int)m.WParam))
  244.                 {
  245.                     case CBN_DROPDOWN:
  246.                         AutoDropDown();
  247.                         return;
  248.                     case CBN_CLOSEUP:
  249.                         if ((DateTime.Now - m_sShowTime).Seconds > 1)
  250.                             HideDropDown();
  251.                         return;
  252.                 }
  253.             }
  254.             base.WndProc(ref m);
  255.         }
  256.         #endregion
  257.         #region Enumerations
  258.         public enum SizeMode
  259.         {
  260.             UseComboSize,
  261.             UseControlSize,
  262.             UseDropDownSize,
  263.         }
  264.         #endregion
  265.         #region Properties
  266.         /// <summary>
  267.         /// Drop-down container. Equals drop-down control when no container is specified.
  268.         /// </summary>
  269.         [Browsable(false)]
  270.         public Control DropDownContainer
  271.         {
  272.             get { return this.m_dropDownCnt; }
  273.         }
  274.         /// <summary>
  275.         /// Actual drop-down control itself.
  276.         /// </summary>
  277.         [Browsable(false)]
  278.         public Control DropDownControl
  279.         {
  280.             get { return m_dropDownCtrl; }
  281.             set { AssignControl(value); }
  282.         }
  283.         /// <summary>
  284.         /// Indicates if drop-down is currently shown.
  285.         /// </summary>
  286.         [Browsable(false)]
  287.         public bool IsDroppedDown
  288.         {
  289.             get { return this.m_bDroppedDown && DropDownContainer.Visible; }
  290.         }
  291.         /// <summary>
  292.         /// Indicates if drop-down is resizable.
  293.         /// </summary>
  294.         [Category("Custom Drop-Down"), Description("Indicates if drop-down is resizable.")]
  295.         public bool AllowResizeDropDown
  296.         {
  297.             get { return this.m_bIsResizable; }
  298.             set
  299.             {
  300.                 if (value != this.m_bIsResizable)
  301.                 {
  302.                     this.m_bIsResizable = value;
  303.                     if (m_dropDownHost != null)
  304.                         UpdateDropDownHost(DropDownControl);
  305.                 }
  306.             }
  307.         }
  308.         /// <summary>
  309.         /// Indicates current sizing mode.
  310.         /// </summary>
  311.         [Category("Custom Drop-Down"), Description("Indicates current sizing mode."), DefaultValue(SizeMode.UseComboSize)]
  312.         public SizeMode DropDownSizeMode
  313.         {
  314.             get { return this.m_sizeMode; }
  315.             set
  316.             {
  317.                 if (value != this.m_sizeMode)
  318.                 {
  319.                     this.m_sizeMode = value;
  320.                     AutoSizeDropDown();
  321.                 }
  322.             }
  323.         }
  324.         [Category("Custom Drop-Down")]
  325.         public Size DropSize
  326.         {
  327.             get { return m_sizeCombo; }
  328.             set
  329.             {
  330.                 m_sizeCombo = value;
  331.                 if (DropDownSizeMode == SizeMode.UseDropDownSize)
  332.                     AutoSizeDropDown();
  333.             }
  334.         }
  335.         [Category("Custom Drop-Down"), Browsable(false)]
  336.         public Size ControlSize
  337.         {
  338.             get { return m_sizeOriginal; }
  339.             set
  340.             {
  341.                 m_sizeOriginal = value;
  342.                 if (DropDownSizeMode == SizeMode.UseControlSize)
  343.                     AutoSizeDropDown();
  344.             }
  345.         }
  346.         #endregion
  347.         #region Hide some unwanted properties
  348.         [Browsable(false)]
  349.         public new ObjectCollection Items
  350.         {
  351.             get { return base.Items; }
  352.         }
  353.         [Browsable(false)]
  354.         public new int ItemHeight
  355.         {
  356.             get { return base.ItemHeight; }
  357.             set { base.ItemHeight = value; }
  358.         }
  359.         #endregion
  360.         #region Attributes
  361.         /// <summary>
  362.         /// Drop-down control host.
  363.         /// </summary>
  364.         ToolStripControlHost m_dropDownHost;
  365.         /// <summary>
  366.         /// Toolstrip drop-down container.
  367.         /// </summary>
  368.         ToolStripDropDown m_dropDown = new ToolStripDropDown();
  369.         /// <summary>
  370.         /// Drop-down container. Equals drop-down control when no container is specified.
  371.         /// </summary>
  372.         Control m_dropDownCnt;
  373.         /// <summary>
  374.         /// Actual drop-down control itself.
  375.         /// </summary>
  376.         Control m_dropDownCtrl;
  377.         /// <summary>
  378.         /// Indicates if drop-down is currently shown.
  379.         /// </summary>
  380.         bool m_bDroppedDown = false;
  381.         /// <summary>
  382.         /// Indicates current sizing mode.
  383.         /// </summary>
  384.         SizeMode m_sizeMode = SizeMode.UseComboSize;
  385.         /// <summary>
  386.         /// Time drop-down was last hidden.
  387.         /// </summary>
  388.         DateTime m_lastHideTime = DateTime.Now;
  389.         /// <summary>
  390.         /// Automatic focus timer helps make sure drop-down control is focused for user
  391.         /// input upon drop-down.
  392.         /// </summary>
  393.         Timer m_timerAutoFocus;
  394.         /// <summary>
  395.         /// Original size of control dimensions when first assigned.
  396.         /// </summary>
  397.         Size m_sizeOriginal = new Size(1, 1);
  398.         /// <summary>
  399.         /// Original size of combo box dropdown when first assigned.
  400.         /// </summary>
  401.         Size m_sizeCombo;
  402.         /// <summary>
  403.         /// Indicates if drop-down is resizable.
  404.         /// </summary>
  405.         bool m_bIsResizable = true;
  406.         #endregion
  407.     }
  408.     /// <summary>
  409.     /// Interface of a drop down container.
  410.     /// </summary>
  411.     public interface IDropDownContainer
  412.     {
  413.         Size CalculateContainerSize(Size controlSize);
  414.     }
  415.     /// <summary>
  416.     /// Provides resizable drop-down container for use in <c>CustomComboBox</c>.
  417.     /// </summary>
  418.     public class ResizableDropDownContainer : Control, IDropDownContainer
  419.     {
  420.         #region Construction and destruction
  421.         public ResizableDropDownContainer(Control childControl)
  422.         {
  423.             if (childControl == null)
  424.                 throw new NullReferenceException();
  425.             this.Controls.Add(childControl);
  426.             m_ctrlChild = childControl;
  427.             // The minimum size of this container must allow for resizable corner.
  428.             this.MinimumSize = new Size(childControl.MinimumSize.Width, childControl.MinimumSize.Height + 18);
  429.         }
  430.         #endregion
  431.         #region IDropDownContainer Members
  432.         public Size CalculateContainerSize(Size controlSize)
  433.         {
  434.             controlSize.Height += 18;
  435.             return controlSize;
  436.         }
  437.         #endregion
  438.         #region Control overrides
  439.         protected override void OnPaint(PaintEventArgs e)
  440.         {
  441.             base.OnPaint(e);
  442.             // Draw resize grip.
  443.             Rectangle rectGrip = new Rectangle(Right - 16, Bottom - 16, 16, 16);
  444.             ControlPaint.DrawSizeGrip(e.Graphics, BackColor, rectGrip);
  445.         }
  446.         protected override void OnSizeChanged(EventArgs e)
  447.         {
  448.             base.OnSizeChanged(e);
  449.             // Position child control.
  450.             ChildControl.Left = ChildControl.Top = 0;
  451.             ChildControl.Width = DisplayRectangle.Width;
  452.             ChildControl.Height = DisplayRectangle.Height - 18;
  453.         }
  454.         protected override void OnMouseDown(MouseEventArgs e)
  455.         {
  456.             base.OnMouseDown(e);
  457.             if (e.Button == MouseButtons.Left)
  458.             {
  459.                 Rectangle rectGrip = CalculateGripRectangle();
  460.                 if (rectGrip.Contains(e.Location))
  461.                 {
  462.                     m_bDragGrip = Capture = true;
  463.                     UpdateCursor(e.Location);
  464.                     // Store anchor position.
  465.                     m_ptAnchor = e.Location;
  466.                 }
  467.             }
  468.         }
  469.         protected override void OnMouseMove(MouseEventArgs e)
  470.         {
  471.             base.OnMouseMove(e);
  472.             if (e.Button == MouseButtons.Left)
  473.             {
  474.                 if (IsDraggingGrip)
  475.                 {
  476.                     // Adjust container dimensions.
  477.                     Size = new Size(Width + e.X - m_ptAnchor.X, Height + e.Y - m_ptAnchor.Y);
  478.                     m_ptAnchor = e.Location;
  479.                     // Invalidate container!
  480.                     Invalidate();
  481.                 }
  482.             }
  483.             if (!IsDraggingGrip)
  484.                 UpdateCursor(e.Location);
  485.         }
  486.         protected override void OnMouseUp(MouseEventArgs e)
  487.         {
  488.             base.OnMouseUp(e);
  489.             m_bDragGrip = Capture = false;
  490.             UpdateCursor(e.Location);
  491.         }
  492.         protected override void OnMouseLeave(EventArgs e)
  493.         {
  494.             base.OnMouseLeave(e);
  495.             // Use default cursor by default.
  496.             Cursor = Cursors.Default;
  497.         }
  498.         #endregion
  499.         #region Methods
  500.         /// <summary>
  501.         /// Update control cursor according to the location specified.
  502.         /// </summary>
  503.         /// <param name="location">Cursor location.</param>
  504.         protected void UpdateCursor(Point location)
  505.         {
  506.             // Use cursor for bottom-right grip.
  507.             Rectangle rectGrip = CalculateGripRectangle();
  508.             if (rectGrip.Contains(location))
  509.                 Cursor = Cursors.SizeNWSE;
  510.             else
  511.                 Cursor = Cursors.Default;
  512.         }
  513.         /// <summary>
  514.         /// Calculate display rectangle of control grip handle.
  515.         /// </summary>
  516.         /// <returns>Grip handle rectangle.</returns>
  517.         protected Rectangle CalculateGripRectangle()
  518.         {
  519.             return new Rectangle(Right - 16, Bottom - 16, 16, 16);
  520.         }
  521.         #endregion
  522.         #region Properties
  523.         /// <summary>
  524.         /// Child control.
  525.         /// </summary>
  526.         [Browsable(false)]
  527.         public Control ChildControl
  528.         {
  529.             get { return this.m_ctrlChild; }
  530.         }
  531.         /// <summary>
  532.         /// Indicates if resizer grip is currently being dragged.
  533.         /// </summary>
  534.         public bool IsDraggingGrip
  535.         {
  536.             get { return this.m_bDragGrip; }
  537.         }
  538.         #endregion
  539.         #region Attributes
  540.         /// <summary>
  541.         /// Child control.
  542.         /// </summary>
  543.         Control m_ctrlChild;
  544.         /// <summary>
  545.         /// Anchor position used to determine distance whilst dragging resize corner.
  546.         /// </summary>
  547.         Point m_ptAnchor;
  548.         /// <summary>
  549.         /// Indicates if resizer grip is currently being dragged.
  550.         /// </summary>
  551.         bool m_bDragGrip = false;
  552.         #endregion
  553.     }
  554. }