Click here to Skip to main content
15,888,733 members
Articles / Programming Languages / C#
Article

ComboBoxTree

Rate me:
Please Sign up or sign in to vote.
4.32/5 (22 votes)
2 Feb 20042 min read 154.9K   3.7K   66   33
A treeview control that drops down much like a combobox.

Sample Image

Introduction

The ComboBoxTree control expands on Gevik Babakhani's DropDown Treeview control. Nearly 85% of the code has been rewritten. This updated control provides the following enhancements:

  • Treeview can be sized beyond the width of its parents and the form.
  • The sizing grip functionality is more consistent with standard Windows sizing grips.
  • Node selection populates the box when it is a root node, the SelectionSeparator property assigns the separator (i.e. a SelectionSeperator that is a period '.' would generate something like: Node0.subnode1.subnode2.rootnode).
  • Better design-time support including the standard treeview WYSIWYG editor.
  • The selected node box is editable and accessible as a property - however there is no validation on this value when set manually.
  • Simplified, by using regions and less method calls.

Please feel free to post comments, questions or criticism. This is my first Windows control, so feedback is appreciated.

Background

I needed a control for a project that required multiple treeviews but had very limited form space. When I found Gevik's DropDown TreeView, I was relieved to find what I was looking for but soon ran into problems that required attention and hours of code updating.

Originally, I didn't intend to redistribute the updated control, but I wanted to give back as a way of saying thanks. Thanks goes out to The Code Project and the original author - thanks Gevik Babakhani ;-)

To use the control, simply add the ComboBoxTree.dll file to your toolbox and drop the control onto your form. The control should provide most of the treeview functionality you require.

Points of Interest

I was able to get around the "sizing beyond parent" problem by adding the treeview to its own borderless form. My next problem involved making the sizing grip work correctly. I was able to get this going by calculating the offset of the mouse within the sizing grip along with the absolute mouse position and the location of the form containing the treeview. My final big problem was the sizing grip when scroll bars showed. Disabling scroll bars wasn't a solution so my only alternative was to add a margin of space on the bottom and right-hand side. A bit different but definitely functional!

History

  • 01/26/04 Version 1.0 submitted.
  • 02/02/04 Version 1.2 submitted, changes include:
    • Text property is now overridden.
    • AbsoluteChildrenSelectableOnly property option now available, users can now select end nodes only or all nodes.
    • Public ValidateText method now available to validate manually entered text.
    • SelectionSeparator now called BranchSeparater.
    • Minor toolbox category changes.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralI modified your project and update my GITHUB Pin
jash.liao26-Jul-18 20:59
jash.liao26-Jul-18 20:59 
BugSize Bug Pin
Member 1145332622-Dec-15 1:55
Member 1145332622-Dec-15 1:55 
QuestionNo NodeSelectEventHandler event? Pin
locketine17-Dec-12 14:07
locketine17-Dec-12 14:07 
AnswerRe: No NodeSelectEventHandler event? Pin
Member 1145332622-Dec-15 2:16
Member 1145332622-Dec-15 2:16 
QuestionCan't change size Pin
MVictorL18-Nov-12 22:09
MVictorL18-Nov-12 22:09 
AnswerRe: Can't change size Pin
jash.liao29-Jul-18 15:18
jash.liao29-Jul-18 15:18 
QuestionHandling the Enter key Pin
Member 390355925-Oct-12 10:55
Member 390355925-Oct-12 10:55 
AnswerRe: Handling the Enter key Pin
jash.liao29-Jul-18 15:17
jash.liao29-Jul-18 15:17 
QuestionHow to show image of the selected tree item in combobox Pin
Rui Frazao9-Jun-10 6:07
Rui Frazao9-Jun-10 6:07 
GeneralCheckbox With Every Node Pin
Farid_Bilal26-Feb-09 20:58
Farid_Bilal26-Feb-09 20:58 
QuestionHow to populate tree with data from datatable ? Pin
marcelo2312-Jul-08 3:49
marcelo2312-Jul-08 3:49 
QuestionHow can i get DropDownStyle like combobox? Pin
Mansoor Mehmood10-Apr-08 3:07
Mansoor Mehmood10-Apr-08 3:07 
GeneralBranchSeparator as String, DropDownHeight, ... Pin
fraveu27-Sep-07 9:53
fraveu27-Sep-07 9:53 
GeneralRe: BranchSeparator as String, DropDownHeight, ... Pin
fraveu27-Sep-07 9:55
fraveu27-Sep-07 9:55 
using System;<br />
using System.ComponentModel;<br />
using System.Drawing;<br />
using System.Resources;<br />
using System.Text.RegularExpressions;<br />
using System.Windows.Forms;<br />
<br />
namespace Verlinea.ComboBoxTree {<br />
    public delegate void NodeSelectEventHandler();<br />
    /// <summary><br />
    /// ComboBoxTree control is a treeview that drops down much like a combobox<br />
    /// </summary><br />
    public class ComboBoxTree : UserControl {<br />
        #region Private Fields<br />
        private Panel pnlBack;<br />
        private Panel pnlTree;<br />
        private TextBox tbSelectedValue;<br />
        private ButtonEx btnSelect;<br />
        private TreeView tvTreeView;<br />
        private LabelEx lblSizingGrip;<br />
        private Form frmTreeView;<br />
<br />
        private string _branchSeparator;<br />
        private bool _absoluteChildrenSelectableOnly;<br />
        private int _dropDownHeight;<br />
        private System.Drawing.Point DragOffset;<br />
        #endregion<br />
        #region Public Properties<br />
        [Browsable(true), Description("Gets the TreeView Nodes collection"), Category("TreeView"), DesignerSerializationVisibility(DesignerSerializationVisibility.Content), Editor(typeof(TreeNodeCollection), typeof(TreeNodeCollection))]<br />
        public TreeNodeCollection Nodes {<br />
            get {<br />
                return this.tvTreeView.Nodes;<br />
            }<br />
        }<br />
<br />
        [Browsable(true), Description("Gets or sets the TreeView's Selected Node"), Category("TreeView")]<br />
        public TreeNode SelectedNode {<br />
            set {<br />
                this.tvTreeView.SelectedNode = value;<br />
            }<br />
        }<br />
<br />
        [Browsable(true), Description("Gets or sets the TreeView's Selected Node"), Category("TreeView")]<br />
        public ImageList Imagelist {<br />
            get { return this.tvTreeView.ImageList; }<br />
            set { this.tvTreeView.ImageList = value; }<br />
        }<br />
<br />
        [Browsable(true), Description("The text in the ComboBoxTree control"), Category("Appearance")]<br />
        public override string Text {<br />
            get { return this.tbSelectedValue.Text; }<br />
            set { this.tbSelectedValue.Text = value; }<br />
        }<br />
<br />
        [Browsable(true), Description("Gets or sets the separator for the selected node's value"), Category("Appearance")]<br />
        public string BranchSeparator {<br />
            get { return this._branchSeparator; }<br />
            set {<br />
                if (value.Length > 0)<br />
                    this._branchSeparator = value/*.Substring(0,1)*/;<br />
            }<br />
        }<br />
<br />
        [Browsable(true), Description("Gets or sets the separator for the selected node's value"), Category("Behavior")]<br />
        public bool AbsoluteChildrenSelectableOnly {<br />
            get { return this._absoluteChildrenSelectableOnly; }<br />
            set { this._absoluteChildrenSelectableOnly = value; }<br />
        }<br />
<br />
        [Browsable(true), Description("The heigth, in pixels, of the treeview in a combobox."), Category("Behavior")]<br />
        public int DropDownHeight {<br />
            get { return this._dropDownHeight; }<br />
            set { this._dropDownHeight = value; }<br />
        }<br />
        #endregion<br />
<br />
        public ComboBoxTree() {<br />
            this.InitializeComponent();<br />
<br />
            // Initializing Controls<br />
            this.pnlBack = new Panel();<br />
            this.pnlBack.BorderStyle = BorderStyle.Fixed3D;<br />
            this.pnlBack.BackColor = Color.White;<br />
            this.pnlBack.AutoScroll = false;<br />
<br />
            this.tbSelectedValue = new TextBox();<br />
            this.tbSelectedValue.BorderStyle = System.Windows.Forms.BorderStyle.None;<br />
<br />
            this.btnSelect = new ButtonEx();<br />
            this.btnSelect.Click += new EventHandler(ToggleTreeView);<br />
            this.btnSelect.FlatStyle = FlatStyle.Flat;<br />
<br />
            this.lblSizingGrip = new LabelEx();<br />
            this.lblSizingGrip.Size = new Size(9, 9);<br />
            this.lblSizingGrip.BackColor = Color.Transparent;<br />
            this.lblSizingGrip.Cursor = Cursors.SizeNWSE;<br />
            this.lblSizingGrip.MouseMove += new MouseEventHandler(SizingGripMouseMove);<br />
            this.lblSizingGrip.MouseDown += new MouseEventHandler(SizingGripMouseDown);<br />
<br />
            this.tvTreeView = new TreeView();<br />
            this.tvTreeView.BorderStyle = BorderStyle.None;<br />
            this.tvTreeView.DoubleClick += new EventHandler(TreeViewNodeSelect);<br />
            this.tvTreeView.Location = new Point(0, 0);<br />
            this.tvTreeView.LostFocus += new EventHandler(TreeViewLostFocus);<br />
            //this.tvTreeView.Scrollable = false;<br />
<br />
            this.frmTreeView = new Form();<br />
            this.frmTreeView.FormBorderStyle = FormBorderStyle.None;<br />
            this.frmTreeView.StartPosition = FormStartPosition.Manual;<br />
            this.frmTreeView.ShowInTaskbar = false;<br />
            this.frmTreeView.BackColor = System.Drawing.SystemColors.Control;<br />
<br />
            this.pnlTree = new Panel();<br />
            this.pnlTree.BorderStyle = BorderStyle.FixedSingle;<br />
            this.pnlTree.BackColor = Color.White;<br />
<br />
            SetStyle(ControlStyles.DoubleBuffer, true);<br />
            SetStyle(ControlStyles.ResizeRedraw, true);<br />
<br />
            // Adding Controls to UserControl<br />
            this.pnlTree.Controls.Add(this.lblSizingGrip);<br />
            this.pnlTree.Controls.Add(this.tvTreeView);<br />
            this.frmTreeView.Controls.Add(this.pnlTree);<br />
            this.pnlBack.Controls.AddRange(new Control[] { btnSelect, tbSelectedValue });<br />
            this.Controls.Add(this.pnlBack);<br />
        }<br />
<br />
        private void RelocateGrip() {<br />
            this.lblSizingGrip.Top = this.frmTreeView.Height - lblSizingGrip.Height - 1;<br />
            this.lblSizingGrip.Left = this.frmTreeView.Width - lblSizingGrip.Width - 1;<br />
        }<br />
<br />
        private void ToggleTreeView(object sender, EventArgs e) {<br />
            if (!this.frmTreeView.Visible) {<br />
                Rectangle CBRect = this.RectangleToScreen(this.ClientRectangle);<br />
                this.frmTreeView.Location = new System.Drawing.Point(CBRect.X, CBRect.Y + this.pnlBack.Height);<br />
<br />
                this.frmTreeView.Show();<br />
                this.frmTreeView.BringToFront();<br />
<br />
                this.RelocateGrip();<br />
                //this.tbSelectedValue.Text = "";<br />
            } else {<br />
                this.frmTreeView.Hide();<br />
            }<br />
        }<br />
<br />
        public bool ValidateText() {<br />
            const char splitChar = '&';<br />
            string ValidatorText = this.Text.Replace(this._branchSeparator, splitChar.ToString());<br />
            TreeNodeCollection TNC = this.tvTreeView.Nodes;<br />
<br />
            for (int i = 0; i < ValidatorText.Split(splitChar/*this._branchSeparator.ToCharArray()[0]*/).Length; i++) {<br />
                bool NodeFound = false;<br />
                string NodeToFind = ValidatorText.Split(splitChar/*this._branchSeparator.ToCharArray()[0]*/)[i];<br />
                for (int j = 0; j < TNC.Count; j++) {<br />
                    if (TNC[j].Text == NodeToFind) {<br />
                        NodeFound = true;<br />
                        TNC = TNC[j].Nodes;<br />
                        break;<br />
                    }<br />
                }<br />
<br />
                if (!NodeFound)<br />
                    return false;<br />
            }<br />
<br />
            return true;<br />
        }<br />
<br />
        #region Events<br />
        private void SizingGripMouseMove(object sender, MouseEventArgs e) {<br />
            if (e.Button == MouseButtons.Left) {<br />
                int TvWidth, TvHeight;<br />
                TvWidth = Cursor.Position.X - this.frmTreeView.Location.X;<br />
                TvWidth = TvWidth + this.DragOffset.X;<br />
                TvHeight = Cursor.Position.Y - this.frmTreeView.Location.Y;<br />
                TvHeight = TvHeight + this.DragOffset.Y;<br />
<br />
                if (TvWidth < this.Width)<br />
                    TvWidth = this.Width;<br />
                if (TvHeight < this._dropDownHeight)<br />
                    TvHeight = this._dropDownHeight;<br />
<br />
                this.frmTreeView.Size = new System.Drawing.Size(TvWidth, TvHeight);<br />
                this.pnlTree.Size = this.frmTreeView.Size;<br />
                this.tvTreeView.Size = new System.Drawing.Size(this.frmTreeView.Size.Width - this.lblSizingGrip.Width, this.frmTreeView.Size.Height - this.lblSizingGrip.Width); ;<br />
                RelocateGrip();<br />
            }<br />
        }<br />
<br />
        private void SizingGripMouseDown(object sender, MouseEventArgs e) {<br />
            if (e.Button == MouseButtons.Left) {<br />
                int OffsetX = System.Math.Abs(Cursor.Position.X - this.frmTreeView.RectangleToScreen(this.frmTreeView.ClientRectangle).Right);<br />
                int OffsetY = System.Math.Abs(Cursor.Position.Y - this.frmTreeView.RectangleToScreen(this.frmTreeView.ClientRectangle).Bottom);<br />
<br />
                this.DragOffset = new Point(OffsetX, OffsetY);<br />
            }<br />
        }<br />
<br />
        private void TreeViewLostFocus(object sender, EventArgs e) {<br />
            if (!this.btnSelect.RectangleToScreen(this.btnSelect.ClientRectangle).Contains(Cursor.Position))<br />
                this.frmTreeView.Hide();<br />
        }<br />
<br />
        private void TreeViewNodeSelect(object sender, EventArgs e) {<br />
            if (this._absoluteChildrenSelectableOnly) {<br />
                if (this.tvTreeView.SelectedNode.Nodes.Count == 0) {<br />
                    tbSelectedValue.Text = this.tvTreeView.SelectedNode.FullPath.Replace(@"\", this._branchSeparator);<br />
                    this.ToggleTreeView(sender, null);<br />
                }<br />
            } else {<br />
                tbSelectedValue.Text = this.tvTreeView.SelectedNode.FullPath.Replace(@"\", this._branchSeparator);<br />
                this.ToggleTreeView(sender, null);<br />
            }<br />
        }<br />
<br />
        private void InitializeComponent() {<br />
            // <br />
            // ComboBoxTree<br />
            // <br />
            this.Name = "ComboBoxTree";<br />
            this._absoluteChildrenSelectableOnly = true;<br />
            this._dropDownHeight = 106;<br />
            this._branchSeparator = "-";<br />
            this.Layout += new System.Windows.Forms.LayoutEventHandler(this.ComboBoxTree_Layout);<br />
        }<br />
<br />
        private void ComboBoxTree_Layout(object sender, System.Windows.Forms.LayoutEventArgs e) {<br />
            this.Height = this.tbSelectedValue.Height + 10;<br />
            this.pnlBack.Size = new Size(this.Width, this.Height - 2);<br />
<br />
            this.btnSelect.Size = new Size(16, this.Height - 6);<br />
            this.btnSelect.Location = new Point(this.Width - this.btnSelect.Width - 4, 0);<br />
<br />
            this.tbSelectedValue.Location = new Point(2, 2);<br />
            this.tbSelectedValue.Width = this.Width - this.btnSelect.Width - 4;<br />
<br />
            this.tvTreeView.Height = this._dropDownHeight;<br />
            this.frmTreeView.Size = new Size(this.Width, this.tvTreeView.Height);<br />
            this.pnlTree.Size = this.frmTreeView.Size;<br />
            this.tvTreeView.Width = this.frmTreeView.Width - this.lblSizingGrip.Width;<br />
            this.tvTreeView.Height = this.frmTreeView.Height - this.lblSizingGrip.Width;<br />
            this.RelocateGrip();<br />
        }<br />
        #endregion<br />
<br />
        #region LabelEx<br />
        private class LabelEx : Label {<br />
            /// <summary><br />
            /// <br />
            /// </summary><br />
            public LabelEx() {<br />
                this.SetStyle(ControlStyles.UserPaint, true);<br />
                this.SetStyle(ControlStyles.DoubleBuffer, true);<br />
                this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);<br />
            }<br />
<br />
            /// <summary><br />
            /// <br />
            /// </summary><br />
            /// <param name="e"></param><br />
            protected override void OnPaint(PaintEventArgs e) {<br />
                base.OnPaint(e);<br />
                System.Windows.Forms.ControlPaint.DrawSizeGrip(e.Graphics, System.Drawing.Color.Black, 1, 0, this.Size.Width, this.Size.Height);<br />
            }<br />
        }<br />
        #endregion<br />
<br />
        #region ButtonEx<br />
        private class ButtonEx : Button {<br />
            ButtonState state;<br />
<br />
            /// <summary><br />
            /// <br />
            /// </summary><br />
            public ButtonEx() {<br />
                this.SetStyle(ControlStyles.UserPaint, true);<br />
                this.SetStyle(ControlStyles.DoubleBuffer, true);<br />
                this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);<br />
<br />
            }<br />
            /// <summary><br />
            /// <br />
            /// </summary><br />
            /// <param name="e"></param><br />
            protected override void OnMouseDown(MouseEventArgs e) {<br />
                state = ButtonState.Pushed;<br />
                base.OnMouseDown(e);<br />
            }<br />
<br />
            /// <summary><br />
            /// <br />
            /// </summary><br />
            /// <param name="e"></param><br />
            protected override void OnMouseUp(MouseEventArgs e) {<br />
                state = ButtonState.Normal;<br />
                base.OnMouseUp(e);<br />
            }<br />
<br />
            /// <summary><br />
            /// <br />
            /// </summary><br />
            /// <param name="e"></param><br />
            protected override void OnPaint(PaintEventArgs e) {<br />
                base.OnPaint(e);<br />
                System.Windows.Forms.ControlPaint.DrawComboButton(e.Graphics, 0, 0, this.Width, this.Height, state);<br />
            }<br />
        }<br />
        #endregion<br />
    }<br />
}

GeneralFlatStyle System Pin
jake07223-Aug-07 10:30
jake07223-Aug-07 10:30 
GeneralRe: FlatStyle System Pin
LAcike@sk30-Jun-08 23:40
LAcike@sk30-Jun-08 23:40 
GeneralAfterSelect event Pin
mayank_max4-Jun-07 6:50
mayank_max4-Jun-07 6:50 
GeneralRe: AfterSelect event Pin
mayank_max4-Jun-07 9:07
mayank_max4-Jun-07 9:07 
GeneralRe: AfterSelect event Pin
mayank_max4-Jun-07 9:13
mayank_max4-Jun-07 9:13 
GeneralRe: AfterSelect event Pin
JossGP11-Oct-07 23:41
JossGP11-Oct-07 23:41 
QuestionHow to sort the ComboBoxTree Pin
satisht23-May-07 20:18
satisht23-May-07 20:18 
GeneralNeed this in VB.net [modified] - no longer needed Pin
Vortran9-Oct-06 10:51
Vortran9-Oct-06 10:51 
GeneralBug Deteced !! Pin
H1berto Ferreira3-Jan-06 1:44
H1berto Ferreira3-Jan-06 1:44 
Generalform lostfocus problem Pin
TurboWang10-Mar-04 14:50
TurboWang10-Mar-04 14:50 
GeneralRe: form lostfocus problem Pin
Gabe Anguiano12-Mar-04 9:59
Gabe Anguiano12-Mar-04 9:59 

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

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