vbAccelerator - Contents of code file: RichEditAutoComplete_DropDownPosition.cs

using System;
using System.Diagnostics;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace vbAccelerator.Components.Controls
{

   /// <summary>
   /// A class for extending a combo box to allow the drop down
   /// to be independently positioned.
   /// </summary>
   /// <remarks>
   /// <para>Use the <see cref="System.Windows.Forms.AssignHandle"/> 
   /// method to connect this instance to a combo box.  If you change a combo
   /// box property which causes its window handle to be recreated (such
   /// as setting the <see cref="System.Windows.Forms.ComboBoxStyle"/>) then
    this class 
   /// will need to be reassigned. You can override the ComboBox's 
   /// <see cref="System.Windows.Forms.ComboBox.OnHandleCreated"/>
   /// to do this assignment automatically.</para>
   /// <para>To show the drop down at a position, use the ComboBox's 
   /// <see cref="System.Windows.Forms.ComboBox.DroppedDown"/> property to set
    <c>true</c>
   /// to drop-down the box, then call the <see
    cref="System.Windows.Forms.ComboBox.Focus"/>
   /// method to ensure keyboard events are passed to the combo box.
   /// </para>
   /// </remarks>   
   public class DropDownPosition : NativeWindow
   {

      #region Unmanaged Code
      /// <summary>
      /// The message which provides the handle of the ListBox
      /// portion of the combo box control.  Can also be used to 
      /// set the background colour of the ListBox.
      /// </summary>
      private const int WM_CTLCOLORLISTBOX = 0x134;

      /// <summary>
      /// Gets the parent the window with the specified handle.
      /// </summary>
      [DllImport("user32")]
      private static extern IntPtr GetParent (IntPtr hWnd);
      /// <summary>
      /// Move a Window with the specified handle to a location.
      /// </summary>
      [DllImport("user32")]      
      private static extern int MoveWindow (IntPtr hwnd, int x, int y, int
       nWidth, int nHeight , bool bRepaint );
      /// <summary>
      /// Returns 1 (<c>true</c>) if the specified <c>hwnd</c> (handle) is a
       Window or not.
      /// </summary>
      [DllImport("user32")]
      private static extern bool IsWindow (IntPtr hwnd );

      /// <summary>
      /// A Win32 API rectangle
      /// </summary>
      [StructLayout(LayoutKind.Sequential)]
      private struct RECT
      {
         public int left;
         public int top;
         public int right;
         public int bottom;
      }
      /// <summary>
      /// Gets the rectangle of a window givent it's handle.
      /// </summary>
      [DllImport("user32")]
      private static extern bool GetWindowRect(IntPtr hwnd, ref RECT lpRect);
      #endregion

      #region Member Variables
      /// <summary>
      /// Where to show the drop-down at
      /// </summary>
      private Point dropDownPosition;
      /// <summary>
      /// Whether to move the drop-down or not.
      /// </summary>
      private bool positionDropDown = false;
      /// <summary>
      /// The window handle of the drop-down list box portion of 
      /// the combo box.
      /// </summary>
      private IntPtr m_hWndDropDown = IntPtr.Zero;
      /// <summary>
      /// Class to detect Combo Close Up events.
      /// </summary>
      private ComboCloseUpDetector closeUpDetector;
      #endregion

      #region Events
      /// <summary>
      /// Raised when the combo box is closed up.
      /// </summary>
      public event System.EventHandler CloseUp;
      #endregion

      /// <summary>
      /// Gets the handle of the drop-down portion of the
      /// assigned combo box if any.  The handle is only
      /// correct once the combo box has been dropped down
      /// at least once.
      /// </summary>
      public IntPtr DropDownHandle
      {
         get
         {
            return this.m_hWndDropDown;
         }
      }
      
      /// <summary>
      /// Gets/sets whether this class wil position the 
      /// drop-down or not.
      /// </summary>
      public bool PositionDropDown
      {
         get
         {
            return positionDropDown;
         }
         set
         {
            positionDropDown = value;
         }
      }      

      /// <summary>
      /// Gets/sets the position to show the drop-down at.
      /// </summary>
      public Point DropDownLocation
      {
         get
         {            
            return dropDownPosition;
         }
         set
         {
            dropDownPosition = value;
         }
      }

      /// <summary>
      /// Performs default Window Procedure processing and
      /// allows the drop-down portion of the combo box to
      /// be moved.
      /// </summary>
      /// <param name="m">Window Procedure Message</param>
      protected override void WndProc(ref System.Windows.Forms.Message m)
      {
         base.WndProc(ref m);
         switch (m.Msg)
         {
            case (WM_CTLCOLORLISTBOX):
               m_hWndDropDown = m.LParam;
               if (positionDropDown)
               {
                  if (IsWindow(m_hWndDropDown))
                  {
                     RECT dropDownRect = new RECT();
                     GetWindowRect(m_hWndDropDown, ref dropDownRect);
                     MoveWindow(m_hWndDropDown, 
                        dropDownPosition.X, dropDownPosition.Y, 
                        dropDownRect.right - dropDownRect.left, 
                        dropDownRect.bottom - dropDownRect.top, 
                        true);
                  }
               }
               break;
         }
      }

      private void closeUpDetector_CloseUp(object sender, EventArgs e)
      {
         if (this.CloseUp != null)
         {
            this.CloseUp(this, e);
         }
      }

      /// <summary>
      /// Assigns this class to the specified ComboBox window handle
      /// and enables tracking for drop-downs and 
      /// </summary>
      /// <param name="handle"></param>
      public new void AssignHandle(IntPtr handle)
      {
         base.AssignHandle(handle);
         closeUpDetector = new ComboCloseUpDetector(handle);
         closeUpDetector.AssignHandle(GetParent(handle));
         closeUpDetector.CloseUp += new EventHandler(closeUpDetector_CloseUp);
      }


      /// <summary>
      /// Default constructor for class
      /// </summary>
      public DropDownPosition()
      {
         // intentionally blank
      }
   }
}