using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace TB.ExtComboBox{
public class Form : System.Windows.Forms.Form {
private System.ComponentModel.IContainer components;
private COMBOBOXINFO cbi;
private System.Windows.Forms.Button btnCancel;
private System.Windows.Forms.Label label1;
private int pixelWidth = -1;
private RECT cboListRect;
private SCROLLINFO si;
private SubClass scList = null;
private int xMaxScroll = 0;
private int xCurrentScroll = 0;
private int xMinScroll = 0;
private int xNewSize = 0;
private System.Windows.Forms.Label label3;
private System.Windows.Forms.ComboBox cboBoxEnhanced;
private System.Windows.Forms.ComboBox cboBoxStandard;
private System.Windows.Forms.Label lblDropDownStyle;
private System.Windows.Forms.Label lblStyle;
private System.Windows.Forms.RadioButton radioBtnDropDown;
private System.Windows.Forms.RadioButton radioBtnDropDownList;
private bool gotCBI = false;
#region Win32 Imports
[DllImport("user32")] public static extern bool GetComboBoxInfo(IntPtr hwndCombo, ref COMBOBOXINFO info);
[DllImport("user32")] public static extern int SendMessage(IntPtr hwnd, int wMsg, int wParam, IntPtr lParam);
[DllImport("user32")] public static extern int GetWindowLong(IntPtr hwnd, int nIndex);
[DllImport("user32")] public static extern int SetWindowLong(IntPtr hwnd, int nIndex, int dwNewLong);
[DllImport("user32")] public static extern int GetClientRect(IntPtr hwnd, ref RECT lpRect);
[DllImport("user32")] public static extern int SetScrollInfo(IntPtr hwnd, int n, ref SCROLLINFO lpcScrollInfo, bool redraw);
#endregion
#region Win32 Constants
public const int WS_HSCROLL = 0x100000;
public const int GWL_STYLE = (-16);
public const int LB_SETHORIZONTALEXTENT = 0x194;
public const int WM_SIZE = 0x5;
public const int WM_HSCROLL = 0x114;
#endregion
#region Scroll Bar Messages - Private Constant Ints
private const int SB_LINEUP = 0;
private const int SB_LINELEFT = 0;
private const int SB_LINEDOWN = 1;
private const int SB_LINERIGHT = 1;
private const int SB_PAGEUP = 2;
private const int SB_PAGELEFT = 2;
private const int SB_PAGEDOWN = 3;
private const int SB_PAGERIGHT = 3;
private const int SB_THUMBPOSITION = 4;
private const int SB_THUMBTRACK = 5;
private const int SB_TOP = 6;
private const int SB_LEFT = 6;
private const int SB_BOTTOM = 7;
private const int SB_RIGHT = 7;
private const int SB_ENDSCROLL = 8;
private const int SB_HORZ = 0;
private const int SB_VERT = 1;
private const int SB_CTL = 2;
private const int SB_BOTH = 3;
private const int SBM_ENABLE_ARROWS = 0x00E4; /*not in win3.1 */
private const int SBM_SETSCROLLINFO = 0x00E9;
private const int SBM_GETSCROLLINFO = 0x00EA;
private const int SIF_RANGE = 0x0001;
private const int SIF_PAGE = 0x0002;
private const int SIF_POS = 0x0004;
private const int SIF_DISABLENOSCROLL = 0x0008;
private const int SIF_TRACKPOS = 0x0010;
private const int SIF_ALL = (SIF_RANGE | SIF_PAGE | SIF_POS | SIF_TRACKPOS);
#endregion
//
#region Form Constructor
public Form() {
InitializeComponent();
InitComboStrings(this.cboBoxEnhanced);
InitComboStrings(this.cboBoxStandard);
//
this.gotCBI = this.InitComboBoxInfo(this.cboBoxEnhanced);
if (this.gotCBI){
this.cboListRect = new RECT();
this.si = new SCROLLINFO();
this.scList = new SubClass(this.cbi.hwndList, false);
this.scList.SubClassedWndProc += new SubClass.SubClassWndProcEventHandler(scList_SubClassedWndProc);
}
}
#endregion
#region Dispose
protected override void Dispose(bool disposing){
if(disposing){
if(components != null){
components.Dispose();
}
if (this.scList != null){
this.scList.ReleaseHandle();
}
}
base.Dispose(disposing);
}
#endregion
#region InitComboBoxInfo Method - Private
private bool InitComboBoxInfo(System.Windows.Forms.ComboBox cbo){
this.cbi = new COMBOBOXINFO();
this.cbi.cbSize = Marshal.SizeOf(this.cbi);
if (!GetComboBoxInfo(cbo.Handle, ref this.cbi)){
return false;
}
return true;
}
#endregion
#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.cboBoxEnhanced = new System.Windows.Forms.ComboBox();
this.btnCancel = new System.Windows.Forms.Button();
this.label1 = new System.Windows.Forms.Label();
this.label3 = new System.Windows.Forms.Label();
this.cboBoxStandard = new System.Windows.Forms.ComboBox();
this.lblDropDownStyle = new System.Windows.Forms.Label();
this.lblStyle = new System.Windows.Forms.Label();
this.radioBtnDropDown = new System.Windows.Forms.RadioButton();
this.radioBtnDropDownList = new System.Windows.Forms.RadioButton();
this.SuspendLayout();
//
// cboBoxEnhanced
//
this.cboBoxEnhanced.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.cboBoxEnhanced.DropDownWidth = 150;
this.cboBoxEnhanced.Location = new System.Drawing.Point(96, 32);
this.cboBoxEnhanced.Name = "cboBoxEnhanced";
this.cboBoxEnhanced.Size = new System.Drawing.Size(112, 21);
this.cboBoxEnhanced.TabIndex = 3;
this.cboBoxEnhanced.DropDown += new System.EventHandler(this.cboBoxEnhanced_DropDown);
this.cboBoxEnhanced.DropDownStyleChanged += new System.EventHandler(this.cboBoxEnhanced_DropDownStyleChanged);
//
// btnCancel
//
this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.btnCancel.Cursor = System.Windows.Forms.Cursors.Hand;
this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
this.btnCancel.FlatStyle = System.Windows.Forms.FlatStyle.Popup;
this.btnCancel.Location = new System.Drawing.Point(130, 104);
this.btnCancel.Name = "btnCancel";
this.btnCancel.TabIndex = 4;
this.btnCancel.Text = "Cancel";
this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click);
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(0, 33);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(98, 16);
this.label1.TabIndex = 2;
this.label1.Text = "Enhanced Combo:";
this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
//
// label3
//
this.label3.AutoSize = true;
this.label3.Location = new System.Drawing.Point(0, 8);
this.label3.Name = "label3";
this.label3.Size = new System.Drawing.Size(93, 16);
this.label3.TabIndex = 0;
this.label3.Text = "Standard Combo:";
this.label3.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
//
// cboBoxStandard
//
this.cboBoxStandard.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.cboBoxStandard.Location = new System.Drawing.Point(96, 8);
this.cboBoxStandard.Name = "cboBoxStandard";
this.cboBoxStandard.Size = new System.Drawing.Size(112, 21);
this.cboBoxStandard.TabIndex = 1;
this.cboBoxStandard.DropDown += new System.EventHandler(this.cboBoxStandard_DropDown);
//
// lblDropDownStyle
//
this.lblDropDownStyle.AutoSize = true;
this.lblDropDownStyle.Location = new System.Drawing.Point(0, 64);
this.lblDropDownStyle.Name = "lblDropDownStyle";
this.lblDropDownStyle.Size = new System.Drawing.Size(89, 16);
this.lblDropDownStyle.TabIndex = 5;
this.lblDropDownStyle.Text = "DropDown Style:";
this.lblDropDownStyle.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
//
// lblStyle
//
this.lblStyle.AutoSize = true;
this.lblStyle.Location = new System.Drawing.Point(88, 64);
this.lblStyle.Name = "lblStyle";
this.lblStyle.Size = new System.Drawing.Size(29, 16);
this.lblStyle.TabIndex = 6;
this.lblStyle.Text = "Style";
this.lblStyle.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
//
// radioBtnDropDown
//
this.radioBtnDropDown.Checked = true;
this.radioBtnDropDown.Location = new System.Drawing.Point(0, 80);
this.radioBtnDropDown.Name = "radioBtnDropDown";
this.radioBtnDropDown.Size = new System.Drawing.Size(80, 18);
this.radioBtnDropDown.TabIndex = 7;
this.radioBtnDropDown.TabStop = true;
this.radioBtnDropDown.Text = "DropDown";
this.radioBtnDropDown.Click += new System.EventHandler(this.radioBtnDropDown_Click);
//
// radioBtnDropDownList
//
this.radioBtnDropDownList.Location = new System.Drawing.Point(96, 80);
this.radioBtnDropDownList.Name = "radioBtnDropDownList";
this.radioBtnDropDownList.Size = new System.Drawing.Size(104, 18);
this.radioBtnDropDownList.TabIndex = 8;
this.radioBtnDropDownList.Text = "DropDownList";
this.radioBtnDropDownList.Click += new System.EventHandler(this.radioBtnDropDown_Click);
//
// Form
//
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.CancelButton = this.btnCancel;
this.ClientSize = new System.Drawing.Size(210, 130);
this.Controls.Add(this.radioBtnDropDownList);
this.Controls.Add(this.radioBtnDropDown);
this.Controls.Add(this.lblStyle);
this.Controls.Add(this.lblDropDownStyle);
this.Controls.Add(this.cboBoxStandard);
this.Controls.Add(this.label3);
this.Controls.Add(this.btnCancel);
this.Controls.Add(this.cboBoxEnhanced);
this.Controls.Add(this.label1);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow;
this.Name = "Form";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "Combo Demo";
this.Load += new System.EventHandler(this.Form_Load);
this.ResumeLayout(false);
}
#endregion
#region BootStrapper
[STAThread]
static void Main() {
Application.Run(new Form());
}
#endregion
#region btnCancel Click Event Handler
private void btnCancel_Click(object sender, System.EventArgs e) {
this.Close();
}
#endregion
#region Initialise the Combo Box with strings
private void InitComboStrings(ComboBox cbo){
cbo.Items.AddRange(new string[]{
"Ottawa, Ontario", "St. John's, Newfoundland and Labrador",
"Halifax, Nova Scotia", "Charlottetown, Prince Edward Island",
"Fredericton, New Brunswick", "Qu�bec, Qu�bec", "Toronto, Ontario",
"Winnipeg, Manitoba", "Regina, Saskatchewan", "Edmonton, Alberta", "Victoria, British Colombia",
"Whitehorse, Yukon", "Yellowknife, Northwest Territories", "Iqaluit, Nunavut",
"lllllllllllllll, lllllllllllllllllllllllll",
"WWWWWWWWWWWWWWW, WWWWWWWWWWWWWWWWWWWWWWWW"});
}
#endregion
#region GetLargestTextExtent - Obtain largest string in pixels
private void GetLargestTextExtent(System.Windows.Forms.ComboBox cbo, ref int largestWidth){
int maxLen = -1;
if (cbo.Items.Count >= 1){
using (Graphics g = cbo.CreateGraphics()){
int vertScrollBarWidth = 0;
if (cbo.Items.Count > cbo.MaxDropDownItems){
vertScrollBarWidth = SystemInformation.VerticalScrollBarWidth;
}
for (int nLoopCnt = 0; nLoopCnt < cbo.Items.Count; nLoopCnt++){
int newWidth = (int) g.MeasureString(cbo.Items[nLoopCnt].ToString(), cbo.Font).Width + vertScrollBarWidth;
if (newWidth > maxLen) {
maxLen = newWidth;
}
}
}
}
largestWidth = maxLen;
}
#endregion
#region cboBoxEnhanced_DropDown Event Handler
private void cboBoxEnhanced_DropDown(object sender, System.EventArgs e) {
this.GetLargestTextExtent(this.cboBoxEnhanced, ref this.pixelWidth);
if ((this.pixelWidth != -1) && (this.pixelWidth > this.cboBoxEnhanced.DropDownWidth) && this.gotCBI){
// Adjust our dropdown list box to include the horizontal scroll!
int listStyle = GetWindowLong(this.cbi.hwndList, GWL_STYLE);
listStyle |= WS_HSCROLL;
listStyle = SetWindowLong(this.cbi.hwndList, GWL_STYLE, listStyle);
// Switch on the Subclassing....
this.scList.SubClassed = true;
// Set the horizontal extent for the listbox!
SendMessage(this.cbi.hwndList, LB_SETHORIZONTALEXTENT, this.pixelWidth, IntPtr.Zero);
}
}
#endregion
#region scList_SubClassedWndProc Event Handler
private void scList_SubClassedWndProc(ref Message m) {
switch (m.Msg){
case WM_SIZE:
GetClientRect(this.cbi.hwndList, ref this.cboListRect);
this.xNewSize = this.scList.LoWord(m.LParam.ToInt32());
this.xMaxScroll = Math.Max(this.pixelWidth - this.xNewSize, 0);
this.xCurrentScroll = Math.Min(this.xCurrentScroll, this.xMaxScroll);
this.si.cbSize = Marshal.SizeOf(this.si);
this.si.nMax = this.xMaxScroll;
this.si.nMin = this.xMinScroll;
this.si.nPos = this.xCurrentScroll;
this.si.nPage = this.xNewSize;
this.si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;
SetScrollInfo(this.cbi.hwndList, SB_HORZ, ref this.si, false);
break;
case WM_HSCROLL:
int xDelta = 0;
int xNewPos = 0;
int modulo = (this.xNewSize > this.pixelWidth) ? (this.xNewSize % this.pixelWidth) : (this.pixelWidth % this.xNewSize);
switch (this.scList.LoWord(m.WParam.ToInt32())){
case SB_PAGEUP:
xNewPos = this.xCurrentScroll - modulo;
break;
case SB_PAGEDOWN:
xNewPos = this.xCurrentScroll + modulo;
break;
case SB_LINEUP:
xNewPos = this.xCurrentScroll - 1;
break;
case SB_LINEDOWN:
xNewPos = this.xCurrentScroll + 1;
break;
case SB_THUMBPOSITION:
xNewPos = this.scList.HiWord(m.WParam.ToInt32());
break;
default:
xNewPos = this.xCurrentScroll;
break;
}
xNewPos = Math.Max(0, xNewPos);
xNewPos = Math.Min(xMaxScroll, xNewPos);
if (xNewPos == this.xCurrentScroll) break;
xDelta = xNewPos - this.xCurrentScroll;
this.xCurrentScroll = xNewPos;
this.si.cbSize = Marshal.SizeOf(this.si);
this.si.fMask = SIF_POS;
this.si.nPos = this.xCurrentScroll;
SetScrollInfo(this.cbi.hwndList, SB_HORZ, ref this.si, true);
break;
}
}
#endregion
#region cboBoxStandard_DropDown Event Handler
private void cboBoxStandard_DropDown(object sender, System.EventArgs e) {
int pw = -1;
this.GetLargestTextExtent(this.cboBoxStandard, ref pw);
this.cboBoxStandard.DropDownWidth = pw;
}
#endregion
// Proof to myself that this darn thing works in a singular code base such as this.
// Irrespective of dropdown styles it works for both DropDown + DropDownList!
#region cboBoxEnhanced_DropDownStyleChanged Event Handler
private void cboBoxEnhanced_DropDownStyleChanged(object sender, System.EventArgs e) {
this.lblStyle.Text = this.cboBoxEnhanced.DropDownStyle.ToString();
}
#endregion
#region radioBtnDropDown_Click Event Handler
private void radioBtnDropDown_Click(object sender, System.EventArgs e) {
if (radioBtnDropDown.Checked){
this.cboBoxEnhanced.DropDownStyle = ComboBoxStyle.DropDown;
}else{
this.cboBoxEnhanced.DropDownStyle = ComboBoxStyle.DropDownList;
}
// We need to call this again since we've changed the dropdown style.
this.gotCBI = this.InitComboBoxInfo(this.cboBoxEnhanced);
if (this.gotCBI){
// No need to call new on scroll class etc..
this.scList = new SubClass(this.cbi.hwndList, false);
this.scList.SubClassedWndProc += new SubClass.SubClassWndProcEventHandler(scList_SubClassedWndProc);
}
}
#endregion
private void Form_Load(object sender, System.EventArgs e) {
radioBtnDropDown.PerformClick();
}
}
#region RECT struct
[StructLayout(LayoutKind.Sequential)]
public struct RECT {
public int Left;
public int Top;
public int Right;
public int Bottom;
}
#endregion
#region COMBOBOXINFO Struct
[StructLayout(LayoutKind.Sequential)]
public struct COMBOBOXINFO {
public int cbSize;
public RECT rcItem;
public RECT rcButton;
public IntPtr stateButton;
public IntPtr hwndCombo;
public IntPtr hwndEdit;
public IntPtr hwndList;
}
#endregion
#region SCROLLINFO struct
[StructLayout(LayoutKind.Sequential)]
public struct SCROLLINFO {
public int cbSize;
public int fMask;
public int nMin;
public int nMax;
public int nPage;
public int nPos;
public int nTrackPos;
}
#endregion
#region SubClass Classing Handler Class
public class SubClass : System.Windows.Forms.NativeWindow{
public delegate void SubClassWndProcEventHandler(ref System.Windows.Forms.Message m);
public event SubClassWndProcEventHandler SubClassedWndProc;
private bool IsSubClassed = false;
public SubClass(IntPtr Handle, bool _SubClass){
base.AssignHandle(Handle);
this.IsSubClassed = _SubClass;
}
public bool SubClassed{
get{ return this.IsSubClassed; }
set{ this.IsSubClassed = value; }
}
protected override void WndProc(ref Message m) {
if (this.IsSubClassed){
OnSubClassedWndProc(ref m);
}
base.WndProc (ref m);
}
#region HiWord Message Cracker
public int HiWord(int Number) {
return ((Number >> 16) & 0xffff);
}
#endregion
#region LoWord Message Cracker
public int LoWord(int Number) {
return (Number & 0xffff);
}
#endregion
#region MakeLong Message Cracker
public int MakeLong(int LoWord, int HiWord) {
return (HiWord << 16) | (LoWord & 0xffff);
}
#endregion
#region MakeLParam Message Cracker
public IntPtr MakeLParam(int LoWord, int HiWord) {
return (IntPtr) ((HiWord << 16) | (LoWord & 0xffff));
}
#endregion
private void OnSubClassedWndProc(ref Message m){
if (SubClassedWndProc != null){
this.SubClassedWndProc(ref m);
}
}
}
#endregion
}