Learn how to create a Fonts Combo, an Image Combo, Align all the Combobox parts, and display colours in a Combobox
Ever needed to do something with the ComboBox, but found that the normal ComboBox is quite limited? The ComboBox, as we know it, shows a list of items. What if you wanted to show colours, fonts, drawn lines, or images with the ComboBox? Wouldn't that be nice? Yes, it would. Not only that, but why not change the appearance and normal behaviour of the normal ComboBox? In this article, I will demonstrate how to do all these and more with the ComboBox.
Your Project: CrazyCombos
As the name implies, you are really going to go a bit crazy with extending the ComboBox's uses.
Create a new Windows Forms project, named CazyCombos.
Fonts Combo
The first thing that I will cover is creating a Font ComboBox. A Font ComboBox displays a list of all the available fonts on the user's PC, similar to what you use to change the Font in MS Word or MS Excel, for example. Apart from listing the available fonts, you also can display a small sample of each particular font by writing the current font name with the current font. For example, if you want to display Comic Sans MS, you must use the Comic Sans MS font to write Comic sans MS; in other words, the name of the font.
To create a Font ComboBox, follow these steps:
- Click Project.
- Click Add Class...
- Name the class FontCbo.cs.
This class will create a new font.
Import the Drawing Namespace at the top of FontCbo.cs.
using System.Drawing; //Import Drawing NameSpace
Create a Font object:
public Font FCFont; //Font Used
Edit the Class constructor as follows:
public FontCbo(Font FCCurrFont)
{
//Set This Font Equal To Font Supplied
FCFont = FCCurrFont;
}
Override the built-in ToString method to display the current font's name:
/// <summary>
/// Override ToString Method To Display Current Font's Name
/// </summary>
/// <returns></returns>
public override string ToString()
{
return FCFont.Name; //Display Font Name
}
Build your project.
Go to frmCrazy's Design view and add a ComboBox named cboFontsCrazy to it. Set cboFontsCrazy's DrawMode property to OwnerDrawVariable. This enables you to display custom items in the list. Add the following variable to frmCrazy:
private SolidBrush FontForeColour; //Font's Colour
Load All the Fonts
In frmCrazy_Load add the following:
//FontCombo
//Obtain & Store System fonts Into Array
FontFamily[] families = FontFamily.Families;
//Loop Through System Fonts
foreach (FontFamily family in families)
{
//Set Current Font's Style To bold
FontStyle style = FontStyle.Bold;
//These Are Only Available In Italic, Not In "Regular",
//So Test For Them, Else, Exception!!
if (family.Name == "Monotype Corsiva"
|| family.Name == "Brush Script MT"
|| family.Name == "Harlow Solid Italic"
|| family.Name == "Palace Script MT" ||
family.Name == "Vivaldi")
{
//Set Style To Italic, To Overt "Regular" & Exception
style = style | FontStyle.Italic;
}
//Display The Font Combo Items
cboFontsCrazy.Items.Add(new FontCbo(new Font(family.Name, 12,
style, GraphicsUnit.Point)));
}
Here, you created an array to store all the system's fonts, and then you looped through all of them. As you loop, you set each font's style to Bold, so that it appears Bold in FontCbo. The interesting thing here to note is that not all of the fonts support the Bold style, not even the Regular font style. What I had to do was to wait for the program to give me errors on the particular font(s) name(s). On each of the fonts in the if block, I received an error that they don't support the regular or Bold styles. This meant that each of those fonts only support italic; that is why I had to identify them, tand hen change their styles to italic. You could have set the style just to italic in the first place, but still you may have received errors on fonts not supporting Bold. So, it is basically trial and error.
The physical drawing of each font happens in the cboFontsCrazy_DrawItem event:
/// <summary>
/// Drawing Of Fonts
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void cboFontsCrazy_DrawItem(object sender,
DrawItemEventArgs e)
{
Brush FontBrush; //Brush To Be used
//If No Current Colour
if (FontForeColour == null)
{
//Set ForeColour
FontForeColour = new SolidBrush(e.ForeColor);
}
else
{
//Fore Colour Changed, Create New Brush
if (!FontForeColour.Color.Equals(e.ForeColor))
{
FontForeColour.Dispose(); //Dispose Old Brush
//Create New Brush
FontForeColour = new SolidBrush(e.ForeColor);
}
}
//Set Appropriate Brush
if ((e.State & DrawItemState.Selected) ==
DrawItemState.Selected)
{
FontBrush = SystemBrushes.HighlightText;
}
else
{
FontBrush = FontForeColour;
}
//Current item's Font
Font font = ((FontCbo)cboFontsCrazy.Items[e.Index]).FCFont;
e.DrawBackground(); //Redraw Item Background
//Draw Current Font
e.Graphics.DrawString(font.Name, font, FontBrush, e.Bounds.X,
e.Bounds.Y);
e.DrawFocusRectangle(); //Draw Focus Rectangle Around It
}
Edit the cboFontsCrazy_MeasureItem event to look like:
/// <summary>
/// Item Height Measurements
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void cboFontsCrazy_MeasureItem(object sender,
MeasureItemEventArgs e)
{
//Get Current Font In ComboBox
Font font = ((FontCbo)cboFontsCrazy.Items[e.Index]).FCFont;
//determine Its Size
SizeF stringSize = e.Graphics.MeasureString(font.Name, font);
//Set Appropriate Height
e.ItemHeight = (int)stringSize.Height;
//Set Appropriate Width
e.ItemWidth = (int)stringSize.Width;
}
You can build and run your program now, and test your Fonts ComboBox. If everything went well, you should be able to display and select Fonts, as displayed in Figure 1.
Figure 1: The Working Fonts ComboBox
Displaying Different Drawn Lines in a ComboBox
If you have seen the Underline Styles ComboBox in MS Word, you know that it displays various line drawings to be used as underlines. You will attempt this next, although it won't be as advanced as the Line ComboBox from MS Word, it stukk will give you the platform to work from. The first step is to add a new class, called LineCbo.cs, to your project.
Make sure you have all the necessary NameSpaces in your class:
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
Make sure your LineCbo class inherits from ComboBox:
namespace System.Windows.Forms
{
class LineCbo : ComboBox
{
Declare the following:
//Get Current System.Drawing.Drawing2D.DashStyles
private string[] DashStyles = Enum.GetNames(typeof(DashStyle));
private int i = 0; //Counter
You need to create an array to store all available Dash styles; you get this from System.Drawing.Drawing2D.DashStyles. This will give you dashed, dotted, and thick lines. Then, you create a variable to be used just in a loop to loop through each dash style.
Modify the Constructor to look like:
/// <summary>
/// Constructor Setting Startup Settings
/// </summary>
public LineCbo()
{
this.DrawMode = DrawMode.OwnerDrawFixed; //DrawMode
//Style Of ComboBox
this.DropDownStyle = ComboBoxStyle.DropDownList;
LCFillLineTypes(); //Call FillLineTypes Method
}
You set the ComboBox's style and DrawMode properties and called the LCFillLineTypes procedure. It looks like this:
/// <summary>
/// Adds All DashStyles To List
/// </summary>
private void LCFillLineTypes()
{
this.Items.Clear(); //Clear All Items
//Loop Through All DashStyles
for (int i = 0; i <= DashStyles.Length - 1; i++)
{
this.Items.Add(DashStyles[i]); //Add Each DashStyle
}
}
The LCFillLineTypes procedure simply loops through the DashStyles enumeration, and adds each item found to the list. Without the aid of overriding the OnDrawItem event, the actual showing of the lines will not happen. Add the following to your class:
/// <summary>
/// Override OnDrawItem, To Draw The Lines
/// </summary>
/// <param name="e"></param>
protected override void
OnDrawItem(System.Windows.Forms.DrawItemEventArgs e)
{
base.OnDrawItem(e);
if (e.Index >= 0) //If Valid List
{
//Create New rectangle For New Item
Rectangle LCCurrItemRect = e.Bounds;
Pen LCDrawPen = new Pen(Color.Black, 2); //Set Drawing Pen
if (i <= DashStyles.Length - 1) //If Valid DashStyle
{
//Set Pen's DashStyle To Current DashStyle
LCDrawPen.DashStyle =
(DashStyle)System.Enum.Parse(typeof(DashStyle),
DashStyles[i]);
//Draw The Current Line With Appropriate DashStyle
e.Graphics.DrawLine(LCDrawPen,
Convert.ToSingle(LCCurrItemRect.X),
Convert.ToSingle(LCCurrItemRect.Y +
LCCurrItemRect.Height / 2),
Convert.ToSingle(LCCurrItemRect.X + LCCurrItemRect.Width),
Convert.ToSingle(LCCurrItemRect.Y +
LCCurrItemRect.Height / 2));
i++; //Increment Counter
}
if (i >= DashStyles.Length - 1) //If Out of Range
i = 0; //Reset to 0, To Loop the Drawing
}
}
In the OnDrawItem event, you created a Rectangle to draw onto, and also a Pen to draw with. You loop through the DashStyle array, and as you loop, you cast the current found dash style so that you can set the Pen's DrawStyle property to the correct current dashstyle present in the array. After you have cast the pen, you simply draw the line with the current dashstyle. I have opted to continue looping through the arrays to keep drawing as needed; you can, of course, opt not to.
Build your program.
Once built (and without errors), you will notice that the LineCbo component was added to your toolbox as shown in Figure 2. You can now add it from the toolbox, to your form and name it lineCboCrazy, or anything else descriptive. If you run your program now, you will find that the LineComboBox functions as intended, as shown in Figure 3.
[CrazyComboCompontents.png]
Figure2: LineCbo Added To The Toolbox
[Lines.png]
Figure 3: The Working Lines ComboBox
Image Combo
Since I began working with .NET, I've always been disappointed with .NET not having a ImageComboBox. That is why I decided to make my own. Create a new class named ImgCbo.cs.
The only NameSpace you need is the System.Drawing NameSpace, and ImgCbo also needs to inherit ComboBox, as listed next:
using System.Drawing;
namespace System.Windows.Forms
{
class ImgCbo : ComboBox //Inherits ComboBox
{
Create an ImageList object. Use it to store your images:
//Create New ImageList
private ImageList ICImagList = new ImageList();
Set the DrawMode in the constructor:
public ImgCbo()
{
//Set DrawMode
this.DrawMode = DrawMode.OwnerDrawFixed;
}
Create an ImageList property:
/// <summary>
/// ImageList Property
/// </summary>
public ImageList ICImageList
{
get
{
return ICImagList; //Get value
}
set
{
ICImagList = value; //Set Value
}
}
Override OnDrawItem, to be able to draw images, text, and font formatting:
/// <summary>
/// Override OnDrawItem, To Be able To Draw Images, Text,
/// And Font Formatting
/// </summary>
/// <param name="e"></param>
protected override void OnDrawItem(DrawItemEventArgs e)
{
e.DrawBackground(); //Draw Background Of Item
e.DrawFocusRectangle(); //Draw Its rectangle
if (e.Index < 0) //Do We Have A Valid List?
//Just Draw Indented Text
e.Graphics.DrawString(this.Text, e.Font,
new SolidBrush(e.ForeColor), e.Bounds.Left +
ICImagList.ImageSize.Width, e.Bounds.Top);
else //We Have A List
{
//Is It A ImageCombo Item?
if (this.Items[e.Index].GetType() == typeof(ICItem))
{
//Get Current Item
ICItem ICCurrItem = (ICItem)this.Items[e.Index];
//Obtain Current Item's ForeColour
Color ICCurrForeColour = (ICCurrItem.ICForeColour !=
Color.FromKnownColor(KnownColor.Transparent))
? ICCurrItem.ICForeColour : e.ForeColor;
//Obtain Current Item's Font
Font ICCurrFont = ICCurrItem.ICHighLight ?
new Font(e.Font, FontStyle.Bold) : e.Font;
//If In Actual List (Which Needs Images)
if (ICCurrItem.ICImageIndex != -1)
{
//Draw Image
this.ICImageList.Draw(e.Graphics, e.Bounds.Left,
e.Bounds.Top, ICCurrItem.ICImageIndex);
//Then, Draw Text In Specified Bounds
e.Graphics.DrawString(ICCurrItem.ICText,
ICCurrFont, new SolidBrush(ICCurrForeColour),
e.Bounds.Left + ICImagList.ImageSize.Width,
e.Bounds.Top);
}
else //No Image Needed, Index = -1
//Just Draw The Indented Text
e.Graphics.DrawString(ICCurrItem.ICText, ICCurrFont,
new SolidBrush(ICCurrForeColour),
e.Bounds.Left + ICImagList.ImageSize.Width, e.Bounds.Top);
}
else //Not An ImageCombo Box Item
//Just Draw The Text
e.Graphics.DrawString(this.Items[e.Index].ToString(),
e.Font, new SolidBrush(e.ForeColor),
e.Bounds.Left + ICImagList.ImageSize.Width, e.Bounds.Top);
}
base.OnDrawItem (e);
}
As with LineCbo, you need to override the OnDrawItem event, so that, in this case, you will be able to display pictures inside the ComboBox, just as a normal ImageComboBox. You have to establish whether or not you have decided to include an image with the item text. If you are displaying text as well as pictures, you need to draw the picture, and then write the text. If you only want to display text, you simply have to draw the text. You can, of course, change the colours of the Item text and make the text Bold or not. You are only half way now, because you still need a class for your ImageCombo's text. Inside ImgCbo, create another class called ICItem. This class will be used to display the actual text and picture(s) in a format you want. Add the following variables to ICItem:
private string ICIText = null //ImageCombo Item Text
private int ICIImageIndex = -1; //Image Combo Item Image Index
private bool ICIHighlight = false; //Highlighted?
//ImageCombo Item ForeColour
private Color ICIForeColour =
Color.FromKnownColor(KnownColor.Transparent);
This is your text, current image index, Boldness setting, and forecolour of your ImgCbo item. Now, create their associated Properties:
/// <summary>
/// ImageCombo Item Text
/// </summary>
public string ICText
{
get
{
return ICIText; //Get Value
}
set
{
ICIText = value; //Set Value
}
}
/// <summary>
/// Image Index
/// </summary>
public int ICImageIndex
{
get
{
return ICIImageIndex; //Get Value
}
set
{
ICIImageIndex = value; //Set Value
}
}
/// <summary>
/// Highlighted ?
/// </summary>
public bool ICHighLight
{
get
{
return ICIHighlight; //Get Value
}
set
{
ICIHighlight = value; //Set Value
}
}
/// <summary>
/// ForeColour
/// </summary>
public Color ICForeColour
{
get
{
return ICIForeColour; //Get Value
}
set
{
ICIForeColour = value; //Set Value
}
}
If you can remember, in the ImgCbo's OnDrawItem event you established whether or not you want pictures along with text. This decision you create via the use of ICItem's constructor. You can overload it to display text only, to display text and the picture, to be able to change the text to bold, and the forecolour of the current item. Add the following:
public ICItem()
{
}
/// <summary>
/// Text & Image Index Only
/// </summary>
/// <param name="ICIItemText"></param>
/// <param name="ICImageIndex"></param>
//First Overload
public ICItem(string ICIItemText, int ICImageIndex)
{
ICIText = ICIItemText; //Text
ICIImageIndex = ICImageIndex; //Image Index
}
/// <summary>
/// Text, Image Index, Highlight, ForeColour
/// </summary>
/// <param name="ICIItemText"></param>
/// <param name="ICImageIndex"></param>
/// <param name="ICHighLight"></param>
/// <param name="ICForeColour"></param>
//Second Overload
public ICItem(string ICIItemText, int ICImageIndex,
bool ICHighLight, Color ICForeColour)
{
ICIText = ICIItemText; //Text
ICIImageIndex = ICImageIndex; //Image Index
ICIHighlight = ICHighLight; //Highlighted?
ICIForeColour = ICForeColour; //ForeColour
}
Finally, you need to override the ToString method again (as with FontCbo):
/// <summary>
/// Override ToString To Return Item Text
/// </summary>
/// <returns></returns>
public override string ToString()
{
return ICIText;
}
Once built, the ImgCbo component will appear inside your Toolbox. Add this new component to your form and name it imgCboCrazy. You need to add the physical items to your new component in frmCrazy_Load:
private void frmCrazy_Load(object sender, EventArgs e)
{
//Insert Text Only In ImageCombo
imgCboCrazy.Items.Add(new ImgCbo.ICItem("No Icon", -1));
//First Real Item In ImageCombo
imgCboCrazy.Items.Add(new ImgCbo.ICItem("Blue Hills", 0, true,
Color.Green));
//Second ImageCombo Item
imgCboCrazy.Items.Add(new ImgCbo.ICItem("Sunset", 1, false,
Color.Blue));
//Third ImageCombo Item
imgCboCrazy.Items.Add(new ImgCbo.ICItem("Water Lillies", 2,
false, Color.Gray));
//Fourth ImageCombo Item
imgCboCrazy.Items.Add(new ImgCbo.ICItem("Winter", 3, false,
Color.Red));
As you can see with the above "creation methods", the ImageCombo is quite versatile. Test your project now, and you should be greeted with a screen similar to Figure 4:
[Image.png]
Figure 4: The Working Image Combo
Displaying Colours
Add a normal combobox to your form. Name it cboColourCrazy, set its DrawMode property to OwnerDrawVariable, and set its DropDownStyle property to DropDownList. In frmCrazy_Load, add the following:
//Colour Combo
//Add Each Known Colour Into Colour ComboBox
//Get System's Known Colours
foreach (string CurrColourName in
System.Enum.GetNames(typeof(System.Drawing.KnownColor)))
{
//Add Known Colours
cboColourCrazy.Items.Add(Color.FromName(CurrColourName));
}
Modify the cboColourCrazy_MeasureItem event to look like this:
/// <summary>
/// Make Sure All Items Will Fit
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void cboColourCrazy_MeasureItem(object sender,
System.Windows.Forms.MeasureItemEventArgs e)
{
Random ColourRnd = new Random(); //Set A Random value
e.ItemHeight = ColourRnd.Next(20, 40);
}
Modify the cboColourCrazy_DrawItem event to the following:
/// <summary>
/// Paint Background, etc.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void cboColourCrazy_DrawItem(object sender,
System.Windows.Forms.DrawItemEventArgs e)
{
if (e.Index < 0) //Don't We Have A List?
{
e.DrawBackground();
e.DrawFocusRectangle();
return;
}
//Get Colour Object From Items List
Color CurrColour = (Color)cboColourCrazy.Items[e.Index];
//Create A Rectangle To Fit New Item
Rectangle ColourSize = new Rectangle(2, e.Bounds.Top + 2,
e.Bounds.Width, e.Bounds.Height - 2);
Brush ColourBrush; //New Colour Brush To Draw With
e.DrawBackground(); //Draw Item's Background
e.DrawFocusRectangle(); //Draw Item's Focus Rectangle
//If Item Selected
if (e.State == System.Windows.Forms.DrawItemState.Selected)
{
ColourBrush = Brushes.White; //Change To White
}
else
{
ColourBrush = Brushes.Black; //Change Back to Black
}
//Draw New Item Rectangle With Current Colour
e.Graphics.DrawRectangle(new Pen(CurrColour), ColourSize);
//Fill New Item rectangle With Current Colour
e.Graphics.FillRectangle(new SolidBrush(CurrColour),
ColourSize);
//Add Border Around Rectangle
ColourSize.Inflate(1, 1); //Border Size
//Draw New Border
e.Graphics.DrawRectangle(Pens.Black, ColourSize);
//Draw Current Colour Name, In The Middle
e.Graphics.DrawString(CurrColour.Name, cboColourCrazy.Font,
ColourBrush, e.Bounds.Height + 5, ((e.Bounds.Height -
cboColourCrazy.Font.Height) / 2) + e.Bounds.Top);
}
In frmCrazy_Load, you simply got a list of all available colours. In the DrawItem event, you gave each item a background colour of the current colour, and wrote the current colour's name in the middle.
Aligning ComoBox Objects
If you look at a ComboBox, you will notice that it consists of a List, a Drop down button, and optionally a Scrollbar. Wouldn't it be nice to be able to move these parts around? You can play around with it now. Add a new class named ComboAlignSettings.cs, and add the following NameSpace:
//Necessary For APIs
using System.Runtime.InteropServices;
namespace System.Windows.Forms
{
//Inherits ComboBox
public class ComboAlignSettings : ComboBox
{
As you probably know, the System.Runtime.InteropServices NameSpace means only one thing, and that is APIs. Without the use of the Windows API moving the ComboBox Scrollbar to the left, right-aligning the text, and moving the ComboBox, a dropdown button would not be possible. The API helps you get an appropriate handle to the ComboBox "control" you want to move, and then move it. Add all the APIs, Constants, and Structures needed:
//Retrieve Info About Specified Window
[DllImport("user32", CharSet = CharSet.Auto)]
public extern static int GetWindowLong(IntPtr hwnd, int nIndex);
//Change An Attribute Of Specified Window
[DllImport("user32")]
public static extern int SetWindowLong(IntPtr hwnd, int nIndex,
int dwNewLong);
//Retrieve Info About Specified Combo Box
[DllImport("user32.dll")]
public static extern int GetComboBoxInfo(IntPtr hWnd,
ref ComboBoxINFO pcbi);
[StructLayout(LayoutKind.Sequential)]
public struct ComboBoxINFO //Contains ComboBox Status Info
{
public Int32 cbSize;
public RECT rcItem;
public RECT rcButton;
public CASComboBoxButtonState caState;
public IntPtr hwndCombo;
public IntPtr hwndEdit;
public IntPtr hwndList;
}
//Describes Width, Height, And Location Of A Rectangle
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
//Determines Current State Of ComboBox
public enum CASComboBoxButtonState
{
STATE_SYSTEM_NONE = 0,
STATE_SYSTEM_INVISIBLE = 0x00008000,
STATE_SYSTEM_PRESSED = 0x00000008
}
/// <summary>
/// Alignment Enum For Left & Right
/// </summary>
public enum CASAlignment
{
CASLeft = 0,
CASRight = 1
}
//ComboBox Style
private const int GWL_EXSTYLE = -20;
//Right Align Text
private const int WS_EX_RIGHT = 4096;
//Left ScrollBar
private const int WS_EX_LEFTSCROLLBAR = 16384;
//Show Drop Down
private const int CB_SHOWDROPDOWN = 335;
private IntPtr CASHandle; //Handle Of ComboBox
//Alignment Options For Text
private CASAlignment CASList;
//Alignment Options For Button
private CASAlignment CASButton;
//Alignment Options For ScrollBar
private CASAlignment CASScroll;
Edit the Class constructor to look like this:
public ComboAlignSettings()
{
CASHandle = CASGetHandle(this); //Get Handle Of ComboBox
//Set Alignments
CASButton = CASAlignment.CASRight;
CASScroll = CASAlignment.CASRight;
CASList = CASAlignment.CASLeft;
}
In the constructor, I got a Handle to this component, with the use of the CASGetHandle method (which is covered later), and I set the default alignment options for this component. The drop-down button is displayed on the right, the scrollbar is right, and the text is aligned left, like an ordinary ComboBox.
Add the CASGetHandle method:
/// <summary>
/// Retrieves ComboBox Handle
/// </summary>
/// <param name="CASCombo"></param>
/// <returns></returns>
public IntPtr CASGetHandle(ComboBox CASCombo)
{
//New ComboBox Settings Object
ComboBoxINFO CASCBI = new ComboBoxINFO();
//Call In Correct Size
CASCBI.cbSize =
System.Runtime.InteropServices.Marshal.SizeOf(CASCBI);
GetComboBoxInfo(CASCombo.Handle, ref CASCBI); //Obtain Handle
return CASCBI.hwndList; //Return Handle
}
This method uses the ComboBoxINFO structure with the GetComboBoxInfo API to return a valid handle to your ComboBox.
Aligning the Objects
Add the following methods:
/// <summary>
/// Align The ComboBox List
/// </summary>
private void CASAlignList()
{
if (CASHandle != IntPtr.Zero) //If Valid Handle
{
//Get ComboBox Style
int CASStyle = GetWindowLong(CASHandle, GWL_EXSTYLE);
switch (CASList)
{
case CASAlignment.CASRight:
//Align Text To The Right
CASStyle = CASStyle | WS_EX_RIGHT;
break;
}
//Apply On ComboBox
SetWindowLong(CASHandle, GWL_EXSTYLE, CASStyle);
}
}
/// <summary>
/// Align The ComboBox ScrollBar
/// </summary>
private void CASAlignScroll()
{
if (CASHandle != IntPtr.Zero) //If Valid Handle
{
//Get ComboBox Style
int CASStyle = GetWindowLong(CASHandle, GWL_EXSTYLE);
switch (CASScroll)
{
case CASAlignment.CASLeft:
//Align ScrollBare To The Left
CASStyle = CASStyle | WS_EX_LEFTSCROLLBAR;
break;
}
//Apply On ComboBox
SetWindowLong(CASHandle, GWL_EXSTYLE, CASStyle);
}
}
/// <summary>
/// Align The ComboBox Button
/// </summary>
private void CASAlignButton()
{
if (CASHandle != IntPtr.Zero) //If Valid Handle
{
//Get ComboBox Style
int CASStyle = GetWindowLong(this.Handle, GWL_EXSTYLE);
switch (CASButton)
{
case CASAlignment.CASLeft:
//Align ComboBox Button To The Left
CASStyle = CASStyle | WS_EX_RIGHT;
break;
}
//Apply On ComboBox
SetWindowLong(this.Handle, GWL_EXSTYLE, CASStyle);
}
}
With the use of the SetWindowLong API, you are aligning each object as you want. Add these methods' associated properties:
/// <summary>
/// Set Text Alignment
/// </summary>
public CASAlignment CASDropListAlignment
{
get
{
return CASList; //Get Value
}
set
{
if (CASList == value) //If Not Valid Value
return;
CASList = value; //Set Value
CASAlignList(); //Call AlignList Method
}
}
/// <summary>
/// Align ScrollBar
/// </summary>
public CASAlignment CASScrollAlignment
{
get
{
return CASScroll; //Get Value
}
set
{
if (CASScroll == value) //If Not Valid Value
return;
CASScroll = value; //Set Value
CASAlignScroll(); //Call AlignScroll Method
}
}
/// <summary>
/// Align ComboBox Button
/// </summary>
public CASAlignment CASDropButtonAlignment
{
get
{
return CASButton; //Get Value
}
set
{
if (CASButton == value) //If Not Valid Value
return;
CASButton = value; //Set Value
CASAlignButton(); //Call AlignButton Method
}
}
Build your project, and once successfully built, your component should appear in the Toolbox, as displayed in Figure 2.
Making the Alignment ComboBox Work
On your form, add this new component and give it a proper name, such as cboAlignAllCrazy, and add three buttons as well. These buttons will be used to align each part of your custom ComboBox you've just added. Once you have added all these controls, you can switch to code view. Currently, cboAlignAllCrazy does not contain any items, so quickly add some items in frmCrazy_Load:
string[] AllAlignArr = new string[10]; //Array For Combo Items
int intAllLoop; //Loop Counter
//Initiate Loop
for (intAllLoop = 0; intAllLoop <= 9; intAllLoop++)
{
//Fill Each Array Element With Appropriate Text
AllAlignArr[intAllLoop] = "Item " + intAllLoop;
//Display Items In Alignment Combo
cboAlignAllCrazy.Items.Add(AllAlignArr[intAllLoop]);
//Set Alignment Combo's Text To Left (Default)
cboAlignAllCrazy.CASDropListAlignment =
ComboAlignSettings.CASAlignment.CASLeft;
}
Add the alignment code for each button:
/// <summary>
/// Button To Right Align Alignment Combo's Text
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnRightText_Click(object sender, EventArgs e)
{
//If Left
if (cboAlignAllCrazy.CASDropListAlignment ==
ComboAlignSettings.CASAlignment.CASLeft)
{
//Set To Right
cboAlignAllCrazy.CASDropListAlignment =
ComboAlignSettings.CASAlignment.CASRight;
}
}
/// <summary>
/// Button To Left Align Alignment Combo's ScrollBar
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnLeftAlignScroll_Click(object sender, EventArgs e)
{
//If Right Aligned
if (cboAlignAllCrazy.CASScrollAlignment ==
ComboAlignSettings.CASAlignment.CASRight)
{
//Set To Left
cboAlignAllCrazy.CASScrollAlignment =
ComboAlignSettings.CASAlignment.CASLeft;
}
}
/// <summary>
/// Button To Left Align Alignment Combo's Drop Button
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnLeftAlignButton_Click(object sender, EventArgs e)
{
//If Right
if (cboAlignAllCrazy.CASDropButtonAlignment ==
ComboAlignSettings.CASAlignment.CASRight)
{
//Set To Left
cboAlignAllCrazy.CASDropButtonAlignment =
ComboAlignSettings.CASAlignment.CASLeft;
}
}
Run it, and you should be able to align your ComboBox objects as displayed in the next sequence of pictures.
[RightAligned.png]
Figure 5: The Text is Right Aligned
[ScrollLeft.png]
Figure 6: A ScrollBar appears on the Left
[ButtonLeft.png]
Figure 7: The Drop-Down button appears on the Left
Centering ComboBox Text
Luckily, centering the text inside a ComboBox is not that complicated. Add a normal ComboBox to your form, and name it cboCenterCrazy. Edit the cboCenterCrazy_DropDown event to resemble the following:
/// <summary>
/// Center The Text Again
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void cboCenterCrazy_DropDown(object sender, EventArgs e)
{
cboCenterCrazy.Items.Clear(); //Clear ComboBox
string[] stringArr = new string[10]; //New Items
int intLoop; //Loop Counter
for (intLoop = 0; intLoop <= 9; intLoop++) //Start Loop
{
//Add Items To Array
stringArr[intLoop] = "Item " + intLoop;
//Center Align Items Again
cboCenterCrazy.Items.Add(stringArr[intLoop].
PadLeft(((cboCenterCrazy.DropDownWidth / 3)
- (stringArr[intLoop].Length)) / 2));
}
}
Here, I added all its items in the DropDown event, and calculated the exact centered position based on the ComboBox's Dropdown width.
Conclusion
Obviously, you can do so much more with the combobox, but hopefully, now, you are able to do special things with your ComboBoxes. Until next time!