Click here to Skip to main content
11,483,003 members (74,236 online)
Click here to Skip to main content

FontComboBox - a font listing combo box for .NET

, 4 Aug 2002 Ms-PL 185.6K 6.2K 64
Rate this:
Please Sign up or sign in to vote.
A fully owner drawn ComboBox derived class for enumerating and choosing fonts

Overview

FontComboBox is a ComboBox derived class that has been wrapped  into a .NET control library. It will drop down a list of all the true type fonts installed on your machine. There are two styles for the FontComboBox - one in which every font name is rendered using it's font, and the other where every font name is shown using a default font and then some text is shown using the selected font.

Using it in your project

Add the control to your toolbox

Right click on your toolbox and choose the Customize Toolbox option. Browse to the FontCombo.dll file and add it to your toolbox.

Initialize the font combo-boxes

There is a public method named Populate which you need to call on each of your FontComboBox objects.

public Form1()
{
    InitializeComponent();
    fontComboBox1.Populate(false); //Simple
    fontComboBox2.Populate(true); //Elaborate
}

Retrieving the currently selected font

All you need to do is to use the Text property.

private void button1_Click(object sender, System.EventArgs e)
{
    textBox1.Font = new Font(fontComboBox1.Text,10);
    textBox2.Font = new Font(fontComboBox2.Text,10);
}

Class reference

There is just one public method for you to use [other than any public methods or properties available through the base classes]

Populate

public void Populate(bool b)

This will populate the combo box with all true type fonts that are installed on your machine. The bool parameter determines the style of the combo box. Call Populate(false) for a simple style font combo box and call Populate(true) for an elaborate style font combo box. In simple style each font name is rendered using it's font. In elaborate style, each font name is rendered using a default font, but some sample text is rendered using the specific font.

Technical details

Basically this is an owner drawn combo box with the DrawMode set to DrawMode.OwnerDrawVariable. The first thing we need to do would obviously be to enumerate the true type fonts on the machine. This is done as follows [partial code snippet, refer source for complete code]

foreach (FontFamily ff in FontFamily.Families)
{
    if(ff.IsStyleAvailable(FontStyle.Regular))
        Items.Add(ff.Name);                                             
}

Now we need to override OnMeasureItem. This method is called when an owner-drawn combo item needs to be drawn and is called before OnDrawItem. Here we can specify the width, height etc. We make use of Graphics.MeasureString to get the text extend of the rendered text in a specific font.

protected override void OnMeasureItem(
                        MeasureItemEventArgs e)
{   
    ...

    Graphics g = CreateGraphics();
    e.ItemHeight = (int)g.MeasureString(fontstring, 
        new Font(fontstring,10)).Height;
    w = (int)g.MeasureString(fontstring, 
        new Font(fontstring,10)).Width;
    if(both)
    {
        int h1 = (int)g.MeasureString(samplestr, 
            new Font(fontstring,10)).Height;
        int h2 = (int)g.MeasureString(
            Items[e.Index].ToString(), 
            new Font("Arial",10)).Height;
        int w1 = (int)g.MeasureString(samplestr, 
            new Font(fontstring,10)).Width;
        int w2 = (int)g.MeasureString(
            Items[e.Index].ToString(), 
            new Font("Arial",10)).Width;

        ...
    }

    ...
}

Now obviously we handle OnDrawItem where we do our actual drawing stuff.

protected override void OnDrawItem(
                    DrawItemEventArgs e)
{   
    ...

    string fontstring = Items[e.Index].ToString();
    nfont = new Font(fontstring,10);
    Font afont = new Font("Arial",10);

    if(both)
    {
        Graphics g = CreateGraphics();
        int w = (int)g.MeasureString(fontstring, 
            afont).Width;

        if((e.State & DrawItemState.Focus)==0)
        {
            //Normal item
            e.Graphics.FillRectangle(new SolidBrush(
                SystemColors.Window),
                e.Bounds.X+ttimg.Width,e.Bounds.Y,
                e.Bounds.Width,e.Bounds.Height);
            e.Graphics.DrawString(fontstring,afont,
                new SolidBrush(SystemColors.WindowText),
                e.Bounds.X+ttimg.Width*2,e.Bounds.Y);   
            e.Graphics.DrawString(samplestr,nfont,
                new SolidBrush(SystemColors.WindowText),
                e.Bounds.X+w+ttimg.Width*2,e.Bounds.Y); 
        }
        else
        {
            //Highlighted item

            ...
        }   
    }
    else
    {

        if((e.State & DrawItemState.Focus)==0)
        {
            //Normal item
            e.Graphics.FillRectangle(new SolidBrush(
                SystemColors.Window),
                e.Bounds.X+ttimg.Width,e.Bounds.Y,
                e.Bounds.Width,e.Bounds.Height);
            e.Graphics.DrawString(fontstring,nfont,
                new SolidBrush(SystemColors.WindowText),
                e.Bounds.X+ttimg.Width*2,e.Bounds.Y);

        }
        else
        {
            //Highlighted item

            ...
        }           

    }

    ...
}

Acknowledgments

  • Chris Losinger - I wrote this class partially due to my current project's requirements and partially because I was inspired by Chris Losinger's MFC font combo box.
  • Senkwe Chanda - Senkwe's excellent beginner article for making owner drawn combo boxes helped me a great deal.
  • Nnamdi Onyeyiri - Nnamdi was with me on Sonork when I wrote this class and I owe him the tip for using system font colors for highlighting . Initially I was using a hard coded color.

Links

License

This article, along with any associated source code and files, is licensed under The Microsoft Public License (Ms-PL)

Share

About the Author

Nish Nishant

United States United States
Nish Nishant is a Software Architect/Consultant based out of Columbus, Ohio. He has over 15 years of software industry experience in various roles including Lead Software Architect, Principal Software Engineer, and Product Manager. Nish is a recipient of the annual Microsoft Visual C++ MVP Award since 2002 (13 consecutive awards as of 2014).

Nish is an industry acknowledged expert in the Microsoft technology stack. He authored
C++/CLI in Action for Manning Publications in 2005, and had previously co-authored
Extending MFC Applications with the .NET Framework for Addison Wesley in 2003. In addition, he has over 140 published technology articles on CodeProject.com and another 250+ blog articles on his
WordPress blog. Nish is vastly experienced in team management, mentoring teams, and directing all stages of software development.

Contact Nish : You can reach Nish on his google email id voidnish.

Website and Blog

Comments and Discussions

 
Questionthank you! Pin
Misael Moneró Thompson19-Oct-14 17:34
memberMisael Moneró Thompson19-Oct-14 17:34 
QuestionMemory Management Issues Pin
Eric Freed17-Jun-12 20:17
memberEric Freed17-Jun-12 20:17 
GeneralMy vote of 5 Pin
manoj kumar choubey20-Feb-12 2:47
membermanoj kumar choubey20-Feb-12 2:47 
QuestionPopulate doesn't catch new Fonts Pin
Phillip Rumple26-Oct-11 15:20
memberPhillip Rumple26-Oct-11 15:20 
AnswerRe: Populate doesn't catch new Fonts Pin
Member 109185549-Jul-14 7:06
memberMember 109185549-Jul-14 7:06 
SuggestionAnother code change (inspired by Lorad's changes) [modified] Pin
Life Lockszmith10-Aug-11 7:03
memberLife Lockszmith10-Aug-11 7:03 
First of all big big thanks for the article, and the code you supplied.
It was my first time I truly understood the "OwnerDraw" hook, and I think it is the first time I used it correctly.

The changes I introduce are part performance, but also a lot of visual cleaning up and code cleanup that I felt was missed, also made the control a bit more generalized for usage from the Visual Form Designer.
List of changes:
- ComboBox and ToolStripComboBox "flavors:
- Sample text is user customized (also from Visual Designer)
- Font Size of sample is based on control font size
- Fonts are refreshed upon style changes
- all changes are synced, so are "more thread safe".
- Sample text (when used) is displayed on the far side of the list, giving a "cleaner" preview.
- Selected font, does not display Sample Text.
- hope I didn't miss anything....

I'd appreciate a thanks reply if someone finds it useful.
I would very much like a reply from the original poster. Roll eyes | :rolleyes:

Thanks, the code is here below:
using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
 
namespace FontCombo
{
    public class FontComboBox : ComboBox
    {
        [DefaultValue("Sample Text")]
        [Description("Sample Text to add following the font name")]
        public string SampleText
        {
            get { return fontComboBoxHandler.SampleText; }
            set { fontComboBoxHandler.SampleText = value;  }
        }
 
        [DefaultValue(true)]
        [Description("Display Font name in predefined control font, and only Sample Text in rendered font")]
        public bool FontNameInNormalFont
        {
            get { return fontComboBoxHandler.FontNameInNormalFont; }
            set { fontComboBoxHandler.FontNameInNormalFont = value; }
        }
 
        FontComboBoxHandler fontComboBoxHandler;
        public FontComboBox()
        {
            fontComboBoxHandler = new FontComboBoxHandler(this);
        }
    }
 

    public class ToolStripFontComboBox : ToolStripComboBox
    {
        [DefaultValue("Sample Text")]
        [Description("Sample Text to add following the font name")]
        public string SampleText
        {
            get { return fontComboBoxHandler.SampleText; }
            set { fontComboBoxHandler.SampleText = value; }
        }
 
        [DefaultValue(true)]
        [Description("Display Font name in predefined control font, and only Sample Text in rendered font")]
        public bool FontNameInNormalFont
        {
            get { return fontComboBoxHandler.FontNameInNormalFont; }
            set { fontComboBoxHandler.FontNameInNormalFont = value; }
        }
 
        FontComboBoxHandler fontComboBoxHandler;
        public ToolStripFontComboBox()
        {
            fontComboBoxHandler = new FontComboBoxHandler(this.ComboBox);
        }
    }
 
    public class FontComboBoxHandler
    {
        private int maxWidth; // used while calculating width of control, 0 is also the 
                              // signal to refresh the font list.
							  
        bool fontNameInNormalFont = true;
        public bool FontNameInNormalFont
        {
            get { return fontNameInNormalFont; }
            set
            {
                if (value != fontNameInNormalFont)
                {
                    maxWidth = 0;
                    fontNameInNormalFont = value;
                }
            }
        }
 
        private string sampleText;
        public string SampleText
        {
            get { return sampleText; }
            set
            {
                if (sampleText != value)
                {
                    maxWidth = 0;
                    sampleText = value;
                }
            }
        }
 
        private readonly object syncLock = new object();
        private Graphics graphics;
 
        private ComboBox theCtrl;
        public ComboBox TheCtrl { get { return this.theCtrl; } }
        
        public FontComboBoxHandler( ComboBox ComboBoxToAttach )
        {
            this.theCtrl = ComboBoxToAttach;
            this.graphics = theCtrl.CreateGraphics();
 
            theCtrl.MaxDropDownItems = 20;
            theCtrl.IntegralHeight = false;
            theCtrl.Sorted = false;
            theCtrl.DropDownStyle = ComboBoxStyle.DropDownList;
            theCtrl.DrawMode = DrawMode.OwnerDrawVariable;
 
            this.fontNameInNormalFont = false;
            this.sampleText = "Sample Text";
 
            theCtrl.FontChanged += new EventHandler(theCtrl_FontChanged);
            theCtrl.DrawItem += new DrawItemEventHandler(theCtrl_DrawItem);
            theCtrl.DropDown += new EventHandler(theCtrl_DropDown);
            theCtrl.MeasureItem += new MeasureItemEventHandler(theCtrl_MeasureItem);
            theCtrl.SizeChanged += new EventHandler(theCtrl_SizeChanged);
        }
 
        private void Populate()
        {
            lock (syncLock)
            {
                this.maxWidth = 0;
                theCtrl.Items.Clear();
 
                foreach (FontFamily ff in FontFamily.Families)
                {
                    if (ff.IsStyleAvailable(FontStyle.Regular))
                    {
                        theCtrl.Items.Add(new Font(ff, theCtrl.Font.Size));
                    }
                }
            }
        }
 
        public void RefreshFonts()
        {
            lock (syncLock)
            {
                if (0 == theCtrl.Items.Count)
                {
                    Populate();
                }
                else
                {
                    for (int i = theCtrl.Items.Count - 1; i >= 0; --i)
                    {
                        Font f = (Font)theCtrl.Items[i];
                        theCtrl.Items[i] = new Font(f.FontFamily, theCtrl.Font.Size);
                    }
                }
            }
        }
 

void  theCtrl_SizeChanged(object sender, EventArgs e)
{
            lock (syncLock)
            {
                if (0 == maxWidth && theCtrl.SelectedIndex > -1)
                {
                    RefreshFonts();
                }
            }
}
 
void  theCtrl_MeasureItem(object sender, MeasureItemEventArgs e)
{
            lock (syncLock)
            {
                if (e.Index > -1)
                {
                    int w = 0;
                    Font tmpFont = theCtrl.Items[e.Index] as Font;
                    string fontName = tmpFont.Name;
 
                    if (FontNameInNormalFont)
                    {
                        SizeF fontSize = graphics.MeasureString(SampleText, tmpFont);
                        SizeF captionSize = graphics.MeasureString(fontName, theCtrl.Font);
                        e.ItemHeight = (int)Math.Max(fontSize.Height, captionSize.Height);
                        w = (int)(fontSize.Width + captionSize.Width + graphics.MeasureString("   ", tmpFont).Width);
                    }
                    else
                    {
                        SizeF s = graphics.MeasureString(fontName, tmpFont);
                        e.ItemHeight = (int)s.Height;
                        w = (int)s.Width;
                    }
                    maxWidth = Math.Max(maxWidth, w);
                    e.ItemHeight = Math.Min(e.ItemHeight, theCtrl.Height);
                    e.ItemWidth = maxWidth;
                }
            }
        }
void  theCtrl_DropDown(object sender, EventArgs e)
{
            lock (syncLock)
            {
                if (0 == maxWidth)
                {
                    RefreshFonts();
                }
                theCtrl.DropDownWidth = maxWidth + 30;
            }}
 
void  theCtrl_DrawItem(object sender, DrawItemEventArgs e)
{
            lock (syncLock)
            {
                if (e.Index > -1)
                {
                    Font tmpFont = theCtrl.Items[e.Index] as Font;
                    string fontName = tmpFont.Name;
                    Brush backBrush = SystemBrushes.Window;
                    Brush textBrush = SystemBrushes.WindowText;
                    bool fontNameOnly = !FontNameInNormalFont || (DrawItemState.ComboBoxEdit == (e.State & DrawItemState.ComboBoxEdit));
                    Font fontNameFont = FontNameInNormalFont ? theCtrl.Font : tmpFont;
 
                    if (DrawItemState.Focus == (e.State & DrawItemState.Focus))
                    {
                        backBrush = SystemBrushes.Highlight;
                        textBrush = SystemBrushes.HighlightText;
                    }
 
                    if (fontNameInNormalFont)
                    {
                    }
                    e.Graphics.FillRectangle(backBrush, e.Bounds);
                    e.Graphics.DrawString(fontName, fontNameFont, textBrush, e.Bounds.Location);
                    if (!fontNameOnly)
                    {
                        e.Graphics.DrawString(SampleText, tmpFont, textBrush, e.Bounds.Right - 6 - (int)(e.Graphics.MeasureString(SampleText, tmpFont).Width), e.Bounds.Y);
                    }
                }
            }
        }
 
void  theCtrl_FontChanged(object sender, EventArgs e)
{
    lock (syncLock)
    {
        maxWidth = 0;
    }
}
    }
}
Sincerely,
Lockszmith
modified on Thursday, August 11, 2011 9:26 AM

GeneralFontcombobox in vb.net Pin
samuelkod21-May-10 11:59
membersamuelkod21-May-10 11:59 
QuestionGreat work... Pin
amolpbhavsar24-Feb-10 18:53
memberamolpbhavsar24-Feb-10 18:53 
GeneralSet Inital Value Pin
YoonMi10-Oct-07 7:10
memberYoonMi10-Oct-07 7:10 
AnswerRe: set Arial font as the initial value Pin
nipsonanomimata22-Oct-08 0:05
membernipsonanomimata22-Oct-08 0:05 
GeneralRe: set Arial font as the initial value Pin
Larry_Smith_IPro17-Dec-10 11:04
memberLarry_Smith_IPro17-Dec-10 11:04 
GeneralRe: set Arial font as the initial value Pin
nipsonanomimata17-Dec-10 21:36
membernipsonanomimata17-Dec-10 21:36 
GeneralRe: set Arial font as the initial value Pin
Forogar7-Jul-11 10:44
memberForogar7-Jul-11 10:44 
GeneralPlacing this FontComboBox in a ToolStrip Pin
YoonMi9-Oct-07 14:58
memberYoonMi9-Oct-07 14:58 
Generalported to vb Pin
halsoft22-Mar-06 18:22
memberhalsoft22-Mar-06 18:22 
QuestionFont Weight property for the text box control Pin
Baber Saeed15-Jan-06 23:36
memberBaber Saeed15-Jan-06 23:36 
GeneralAutoScale Pin
mmcguirk115-Dec-04 7:24
membermmcguirk115-Dec-04 7:24 
GeneralRe: AutoScale Pin
Larry_Smith_IPro17-Dec-10 10:57
memberLarry_Smith_IPro17-Dec-10 10:57 
GeneralCouple of changes Pin
Lorad14-Oct-04 10:38
memberLorad14-Oct-04 10:38 
GeneralA problem of OwnerDraw ComboBox Pin
sarmoo14-Sep-03 21:30
membersarmoo14-Sep-03 21:30 
GeneralRe: A problem of OwnerDraw ComboBox Pin
James Miles / 20810974310-Nov-04 22:11
memberJames Miles / 20810974310-Nov-04 22:11 
GeneralLayout Mod, editable ComboBox & MS look (yet no history) Pin
mistery2214-Sep-03 6:09
membermistery2214-Sep-03 6:09 
GeneralAny Chance.... Pin
Anonymous14-Jun-03 10:28
sussAnonymous14-Jun-03 10:28 
GeneralLekker Pin
Paul Watson4-Aug-02 23:25
sitebuilderPaul Watson4-Aug-02 23:25 
GeneralNice work ! Pin
Soliant4-Aug-02 22:13
memberSoliant4-Aug-02 22:13 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web04 | 2.8.150520.1 | Last Updated 5 Aug 2002
Article Copyright 2002 by Nish Nishant
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid