Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

The Archive Base

The Archive Base Logo The Archive Base Logo

The Archive Base Navigation

  • Home
  • SEARCH
  • About Us
  • Blog
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • Add group
  • Groups page
  • Feed
  • User Profile
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Buy Points
  • Users
  • Help
  • Buy Theme
  • SEARCH
Home/ Questions/Q 6803583
In Process

The Archive Base Latest Questions

Editorial Team
  • 0
Editorial Team
Asked: May 26, 20262026-05-26T19:21:44+00:00 2026-05-26T19:21:44+00:00

I’m trying to convert Bradley Copers awesome grouped ComboxBox into a GroupedCheckedListBox. But in

  • 0

I’m trying to convert Bradley Copers awesome grouped ComboxBox into a GroupedCheckedListBox. But in the code below OnMeasureItem in never entered. It seems that I can’t change DrawMode to DrawMode.OwnerDrawVariable, stays as Normal.

If I extend a ListBox with the code below. It works nicely. Do I need to implement the CheckBox stuff on my own?

namespace GroupedCheckBoxList.SeoTools.UI.Components
{
    // Based on Bradley Smith's "A ComboBox Control With Grouping"

    class GroupedCheckedListBox : CheckedListBox, IComparer
    {
        private BindingSource mBindingSource;       // used for change detection and grouping
        private Font mGroupFont;                    // for painting
        private string mGroupMember;                // name of group-by property
        private PropertyDescriptor mGroupProperty;  // used to get group-by values
        private ArrayList mInternalItems;           // internal sorted collection of items
        private TextFormatFlags mTextFormatFlags;   // used in measuring/painting

        /// <summary>
        /// Gets or sets the data source for this GroupedComboBox.
        /// </summary>
        public new object DataSource
        {
            get
            {
                // binding source should be transparent to the user
                return (mBindingSource != null) ? mBindingSource.DataSource : null;
            }
            set
            {
                if (value != null)
                {
                    // wrap the object in a binding source and listen for changes
                    mBindingSource = new BindingSource(value, String.Empty);
                    mBindingSource.ListChanged += new ListChangedEventHandler(mBindingSource_ListChanged);
                    SyncInternalItems();
                }
                else
                {
                    // remove binding
                    base.DataSource = mBindingSource = null;
                }
            }
        }

        /// <summary>
        /// Gets a value indicating whether the drawing of elements in the list will be handled by user code. 
        /// </summary>
        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public new DrawMode DrawMode
        {
            get
            {
                return base.DrawMode;
            }
            private set
            {
                base.DrawMode = value;
            }
        }

        /// <summary>
        /// Gets or sets the property to use when grouping items in the list.
        /// </summary>
        public string GroupMember
        {
            get { return mGroupMember; }
            set
            {
                mGroupMember = value;
                if (mBindingSource != null) SyncInternalItems();
            }
        }

        /// <summary>
        /// Initialises a new instance of the GroupedCheckedListBox class.
        /// </summary>
        public GroupedCheckedListBox()
        {
            DrawMode = DrawMode.OwnerDrawVariable; //DOESN'T WORK VALUE STAYS AS NORMAL
            mGroupMember = String.Empty;
            mInternalItems = new ArrayList();
            mTextFormatFlags = TextFormatFlags.EndEllipsis | TextFormatFlags.NoPrefix | TextFormatFlags.SingleLine | TextFormatFlags.VerticalCenter;
        }

        /// <summary>
        /// Explicit interface implementation for the IComparer.Compare method. Performs a two-tier comparison 
        /// on two list items so that the list can be sorted by group, then by display value.
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns></returns>
        int IComparer.Compare(object x, object y)
        {
            // compare the display values (and return the result if there is no grouping)
            int secondLevelSort = Comparer.Default.Compare(GetItemText(x), GetItemText(y));
            if (mGroupProperty == null) return secondLevelSort;

            // compare the group values - if equal, return the earlier comparison
            int firstLevelSort = Comparer.Default.Compare(
                Convert.ToString(mGroupProperty.GetValue(x)),
                Convert.ToString(mGroupProperty.GetValue(y))
            );

            if (firstLevelSort == 0)
                return secondLevelSort;
            else
                return firstLevelSort;
        }

        /// <summary>
        /// Determines whether the list item at the specified index is the start of a new group. In all 
        /// cases, populates the string respresentation of the group that the item belongs to.
        /// </summary>
        /// <param name="index"></param>
        /// <param name="groupText"></param>
        /// <returns></returns>
        private bool IsGroupStart(int index, out string groupText)
        {
            bool isGroupStart = false;
            groupText = String.Empty;

            if ((mGroupProperty != null) && (index >= 0) && (index < Items.Count))
            {
                // get the group value using the property descriptor
                groupText = Convert.ToString(mGroupProperty.GetValue(Items[index]));

                // this item is the start of a group if it is the first item with a group -or- if
                // the previous item has a different group
                if ((index == 0) && (groupText != String.Empty))
                {
                    isGroupStart = true;
                }
                else if ((index - 1) >= 0)
                {
                    string previousGroupText = Convert.ToString(mGroupProperty.GetValue(Items[index - 1]));
                    if (previousGroupText != groupText) isGroupStart = true;
                }
            }
            return isGroupStart;
        }

        /// <summary>
        /// Re-synchronises the internal sorted collection when the data source changes.
        /// </summary>
        private void mBindingSource_ListChanged(object sender, ListChangedEventArgs e)
        {
            SyncInternalItems();
        }

        /// <summary>
        /// When the control font changes, updates the font used to render group names.
        /// </summary>
        protected override void OnFontChanged(EventArgs e)
        {
            base.OnFontChanged(e);
            mGroupFont = new Font(Font, FontStyle.Bold);
        }

        /// <summary>
        /// When the parent control changes, updates the font used to render group names.
        /// </summary>
        protected override void OnParentChanged(EventArgs e)
        {
            base.OnParentChanged(e);
            mGroupFont = new Font(Font, FontStyle.Bold);
        }

        /// <summary>
        /// Performs custom painting for a list item.
        /// </summary>
        protected override void OnDrawItem(DrawItemEventArgs e)
        {
            base.OnDrawItem(e);

            if ((e.Index >= 0) && (e.Index < Items.Count))
            {
                // get noteworthy states
                bool comboBoxEdit = (e.State & DrawItemState.ComboBoxEdit) == DrawItemState.ComboBoxEdit;
                bool selected = (e.State & DrawItemState.Selected) == DrawItemState.Selected;
                bool noAccelerator = (e.State & DrawItemState.NoAccelerator) == DrawItemState.NoAccelerator;
                bool disabled = (e.State & DrawItemState.Disabled) == DrawItemState.Disabled;
                bool focus = (e.State & DrawItemState.Focus) == DrawItemState.Focus;

                // determine grouping
                string groupText;
                bool isGroupStart = IsGroupStart(e.Index, out groupText) && !comboBoxEdit;
                bool hasGroup = (groupText != String.Empty) && !comboBoxEdit;

                // the item text will appear in a different colour, depending on its state
                Color textColor;
                if (disabled)
                    textColor = SystemColors.GrayText;
                /*
                else if ((comboBoxEdit && Focused && !DroppedDown) || selected)
                    textColor = SystemColors.HighlightText;
                */
                else
                    textColor = ForeColor;

                // items will be indented if they belong to a group
                Rectangle itemBounds = Rectangle.FromLTRB(
                    e.Bounds.X + (hasGroup ? 12 : 0),
                    e.Bounds.Y + (isGroupStart ? (e.Bounds.Height / 2) : 0),
                    e.Bounds.Right,
                    e.Bounds.Bottom
                );
                Rectangle groupBounds = new Rectangle(
                    e.Bounds.X,
                    e.Bounds.Y,
                    e.Bounds.Width,
                    e.Bounds.Height / 2
                );

                if (isGroupStart && selected)
                {
                    // ensure that the group header is never highlighted
                    e.Graphics.FillRectangle(SystemBrushes.Highlight, e.Bounds);
                    e.Graphics.FillRectangle(new SolidBrush(BackColor), groupBounds);
                }
                else
                {
                    // use the default background-painting logic
                    e.DrawBackground();
                }

                // render group header text
                if (isGroupStart) TextRenderer.DrawText(
                    e.Graphics,
                    groupText,
                    mGroupFont,
                    groupBounds,
                    ForeColor,
                    mTextFormatFlags
                );

                // render item text
                TextRenderer.DrawText(
                    e.Graphics,
                    GetItemText(Items[e.Index]),
                    Font,
                    itemBounds,
                    textColor,
                    mTextFormatFlags
                );

                // paint the focus rectangle if required
                if (focus && !noAccelerator)
                {
                    if (isGroupStart && selected)
                    {
                        // don't draw the focus rectangle around the group header
                        ControlPaint.DrawFocusRectangle(e.Graphics, Rectangle.FromLTRB(groupBounds.X, itemBounds.Y, itemBounds.Right, itemBounds.Bottom));
                    }
                    else
                    {
                        // use default focus rectangle painting logic
                        e.DrawFocusRectangle();
                    }
                }
            }
        }

        /// <summary>
        /// Determines the size of a list item.
        /// </summary>
        protected override void OnMeasureItem(MeasureItemEventArgs e)
        {
            base.OnMeasureItem(e);

            e.ItemHeight = Font.Height;

            string groupText;
            if (IsGroupStart(e.Index, out groupText))
            {
                // the first item in each group will be twice as tall in order to accommodate the group header
                e.ItemHeight *= 2;
                e.ItemWidth = Math.Max(
                    e.ItemWidth,
                    TextRenderer.MeasureText(
                        e.Graphics,
                        groupText,
                        mGroupFont,
                        new Size(e.ItemWidth, e.ItemHeight),
                        mTextFormatFlags
                    ).Width
                );
            }
        }

        /// <summary>
        /// Rebuilds the internal sorted collection.
        /// </summary>
        private void SyncInternalItems()
        {
            // locate the property descriptor that corresponds to the value of GroupMember
            mGroupProperty = null;
            foreach (PropertyDescriptor descriptor in mBindingSource.GetItemProperties(null))
            {
                if (descriptor.Name.Equals(mGroupMember))
                {
                    mGroupProperty = descriptor;
                    break;
                }
            }

            // rebuild the collection and sort using custom logic
            mInternalItems.Clear();
            foreach (object item in mBindingSource) mInternalItems.Add(item);
            mInternalItems.Sort(this);

            // bind the underlying ComboBox to the sorted collection
            base.DataSource = mInternalItems;
        }
    }
}
  • 1 1 Answer
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

1 Answer

  • Voted
  • Oldest
  • Recent
  • Random
  1. Editorial Team
    Editorial Team
    2026-05-26T19:21:44+00:00Added an answer on May 26, 2026 at 7:21 pm

    It would appear that the CheckedListBox control does not support owner-drawing. From the MSDN documentation, its DrawMode property “…[always returns] a System.Windows.Forms.DrawMode of Normal”.

    While the regular ListBox can be adapted to show items in groups using my technique, the checkbox version cannot.

    You will probably want to use a TreeView control with the CheckBoxes property enabled; it’s the next best thing.

    • 0
    • Reply
    • Share
      Share
      • Share on Facebook
      • Share on Twitter
      • Share on LinkedIn
      • Share on WhatsApp
      • Report

Sidebar

Related Questions

link Im having trouble converting the html entites into html characters, (&# 8217;) i
I have a French site that I want to parse, but am running into
I ran into a problem. Wrote the following code snippet: teksti = teksti.Trim() teksti
I am trying to understand how to use SyndicationItem to display feed which is
Basically, what I'm trying to create is a page of div tags, each has
I want to count how many characters a certain string has in PHP, but
I have a string like this: La Torre Eiffel paragonata all&#8217;Everest What PHP function
this is what i have right now Drawing an RSS feed into the php,
I have this code to decode numeric html entities to the UTF8 equivalent character.
I am trying to render a haml file in a javascript response like so:

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help
  • SEARCH

Footer

© 2021 The Archive Base. All Rights Reserved
With Love by The Archive Base

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.