The Windows Forms ComboBox control is an excellent user interface component for situations where you want to present the user with a discrete set of choices, or invite them to enter an arbitrary string value. It does not pose much of a usability challenge; inherently, the control is intuitive and easy to use. However, there are occasions when the usability of an application can be greatly improved by arranging the user’s options into groups or categories. This is particularly useful when the number of options is large, or when there is a risk of ambiguity in the wording of the options – often GUI designers are locked in a constant battle between brevity and elegance over the risk of ambiguity. By introducing groups/categories, the brevity can be maintained while adding another layer of description.
Unfortunately, the standard ComboBox control has no provision for groups or categories; it is designed to be a flat list with no implied cohesion between the list items, and no hierarchy. Nevertheless, a number of applications (including Mozilla Firefox) have identified the need for a drop-down control with a grouping mechanism, and there are now many examples floating around in application land.
I have written GroupedComboBox in an attempt to provide a solution that is in-keeping with the existing principles of visual design and data binding used throughout Windows Forms. It is a drop-in replacement for the ComboBox, inheriting from it in order to reduce functional duplication. As is the case with much of the extended functionality in the standard ComboBox, the control must be bound to a data source in order to take advantage of grouping. Very little changes to existing code are required in order to use the grouping mechanism:
- Set the value of the new GroupMember property to the name of the property you wish to group the list items by. (This functions in the same manner as the DisplayMember and ValueMember properties, so you can use a property, column name, etc)
- Bind to a suitable data source (Array, IList, IListSource, ITypedList, DataView, etc). (Note: Arrays must be strongly-typed; object[] data sources expose no bindable properties.)
- Due to the implementation, the items in the list will be inherently sorted. Lexiographical sorting is considered to be an aspect of best-practice user interface design. If you have different sorting requirements, consider using a more appropriate control.
Note: The visual style renderer on Windows Vista/7 uses a raised button effect when the DropDownStyle property is set to ComboBoxStyle.DropDownList and the DrawMode property is set to DrawMode.Normal, however it reverts to the classic rendering style when owner-drawing is enabled. There is no simple way to force the system style in this situation, therefore GroupedComboBox may visually clash with System.Windows.Forms.ComboBox if both types of controls appear on the same Form.
Some Background on Windows Forms Data Binding
If you use the data binding features in Windows Forms controls, you may be curious to know how values can be plucked from a data source using the DisplayMember/ValueMember/DataSource mechanism. The items in a data source are not of any one type; depending on the source, they can be primitive data types, objects with public properties, instances of System.Data.DataRowView or even a heterogenous collection of different types of items. The only constraint on data sources, in fact, is that they implement IList or IListSource.
So, given an object of an unpredictable type, how can we get the value of one of its properties? Well, firstly we need to understand the definition of a ‘property’ in the context of data binding; it does not necessarily refer to a get/set-style property, but rather an abstraction of one. This is what allows us to bind to columns in a DataView, or properties that may or may not exist in a heterogenous collection. The System.ComponentModel.PropertyDescriptor class is the mechanism we use to access the values of these abstracted properties.
PropertyDescriptor is an abstract class. Property descriptors are generally obtained from the data source, rather than from the list items themselves. There are different implementations for different data sources; for example, System.ComponentModel.ReflectPropertyDescriptor uses Reflection to crawl a collection’s element types for their public properties, while the DataView uses System.Data.DataColumnPropertyDescriptor to represent its columns.
You can get property descriptors in a number of ways – sources that implement ITypedList expose them directly, while arrays and lists require the ListBindingHelper to retrieve them. Thankfully, the BindingSource component rounds them all up via its GetItemProperties() method. In situations (such as this example) where you have the name of the desired property as a string, you need simply enumerate through the list of property descriptors until you find one with a matching Name value.
// assume 'dataSource' is a data source that implements IList or IListSource and // 'displayMember' is a string representing the property we want BindingSource bindingSource = new BindingSource(dataSource, String.Empty); PropertyDescriptor displayProperty = null; foreach (PropertyDescriptor descriptor in bindingSource.GetItemProperties(null)) { if (descriptor.Name.Equals(displayMember)) { displayProperty = descriptor; break; } }
Once you have an instance of PropertyDescriptor, you can call its GetValue() method for any item in the data source:
// assume 'listItem' is an object obtained from the data source object displayValue = (displayProperty != null) ? displayProperty.GetValue(listItem) : null;
How GroupedComboBox Implements GroupMember
Bearing in mind the above, it is not difficult to extend this functionality to obtain a group-by value for our data source. My implementation shadows the ComboBox control’s DataSource property to transparently ensure that a BindingSource is used:
public new object DataSource { get { return (mBindingSource != null) ? mBindingSource.DataSource : null; } set { mBindingSource = (value != null) ? new BindingSource(value, String.Empty) : null; } }
Having established that a BindingSource component will be available, we can look up the group-by value of each list item by matching the value of GroupMember to the Name property of one of the binding source’s PropertyDescriptor objects. There is, of course, always the chance that the data source will not have a matching property – should this happen, we will assume that a given list item is ungrouped.
Sorting List Items Into Groups
In order to be presented in groups, the list items must be specially sorted – firstly by group (if the list item is grouped), then by display value. We cannot assume that the data source is pre-sorted in this manner, or that it is even sortable at all, therefore we will use an internal sorted collection to enforce the order of the list items. For data sources containing reference types, there is only a small overhead in doing so (though for data sources containing value types, we are essentially duplicating the list). It is this sorted collection that the underlying ComboBox is bound to.
Since we are creating a sorted view of the original data source, the onice is on GroupedComboBox to stay in-sync with it. This is another compelling reason to use a BindingSource, as it provides the ListChanged event for compatible data sources (e.g. DataView). The sorted collection is rebuilt whenever the data source or GroupMember property is changed.
The two-tier sort is performed in the following manner (where ‘mGroupProperty‘ is the PropertyDescriptor object obtained earlier):
int IComparer.Compare(object x, object y) { int secondLevelSort = Comparer.Default.Compare(GetItemText(x), GetItemText(y)); if (mGroupProperty == null) return secondLevelSort; int firstLevelSort = Comparer.Default.Compare( Convert.ToString(mGroupProperty.GetValue(x)), Convert.ToString(mGroupProperty.GetValue(y)) ); return (firstLevelSort == 0) ? secondLevelSort : firstLevelSort; }
The most sensible collection to use internally in this case would be ArrayList, since we must select an IList, we know very little about the elements and we want to sort them.
Painting the List Items
To display the list items in a grouped form, we will have to paint them manually. This is not a hugely daunting task, but care must be taken in order to avoid exception-prone code and maintain the visual principles of Windows Forms controls. We will use the OwnerDrawVariable mode to paint the items, because it allows us to have some items drawn larger than others; in fact, the first item in each group will be twice as tall in order to accommodate the group header.
Given its index in the Items collection, a list item requires a header if:
- It is at position 0 and has a non-empty group-by value, or:
- It has a different group-by value to the item at [index – 1].
The height of an item in the drop-down portion of the ComboBox is the number of lines multiplied by the Height property of the Font. We calculate the size of each list item by handling the MeasureItem event. The item is then painted in the handler for the DrawItem event.
The TextRenderer class is very useful in rendering each item. Painting a list item is more than merely drawing the text, however. There are a number of flags of the DrawItemState enumeration (accessible through DrawItemEventArgs.State) that must influence the drawing code:
- Selected – The mouse is hovering over the item. Under normal circumstances, the background will be painted with SystemBrushes.Highlightand the text will be painted in SystemColors.HighlightText.
- Focus – The item has focus, which usually occurs in conjunction with being selected. A focus rectangle will need to be drawn around it, unless NoAccelerator is set. For new installations of Windows, accelerators are disabled by default.
- ComboBoxEdit – When the ComboBox has its DropDownStyle set to DropDownList, the presence of this flag means that the item is being painted in the main part of the control. Different rules apply here:
- When the control is in focus and the drop-down list is not showing, the item should appear as if it were highlighted.
- When the control is not in focus, or if in focus but dropped down, the item should appear as if it were normal (undecorated).
- In the case of the grouped combo box, we will not indent items or paint headers for groups in this state.
- Disabled – This is found in conjunction with ComboBoxEdit, and indicates that the control’s Enabled property is set to false, therefore the text should be rendered using SystemColors.GrayText.
We have some additional stylistic requirements in this case, such as indenting all items with a non-empty group-by value and giving the impression that the group headers are not selectable (by suppressing the highlight and focus rectangle from them). We also want to render the headings in bold, to differentiate them further from the item text.
Example Usage
The following examples demonstrate how fundamentally different types of data sources can be used to produce the same output:
ArrayList of Anonymous Objects
GroupedComboBox groupedCombo = new GroupedComboBox(); groupedCombo.ValueMember = "Value"; groupedCombo.DisplayMember = "Display"; groupedCombo.GroupMember = "Group"; groupedCombo.DataSource = new ArrayList(new object[] { new { Value=100, Display="Apples", Group="Fruit" }, new { Value=101, Display="Pears", Group="Fruit" }, new { Value=102, Display="Carrots", Group="Vegetables" }, new { Value=103, Display="Beef", Group="Meat" }, new { Value=104, Display="Cucumbers", Group="Vegetables" }, new { Value=0, Display="(other)", Group=String.Empty }, new { Value=105, Display="Chillies", Group="Vegetables" }, new { Value=106, Display="Strawberries", Group="Fruit" } });
DataView
DataTable dt = new DataTable(); dt.Columns.Add("Value", typeof(int)); dt.Columns.Add("Display"); dt.Columns.Add("Group"); dt.Rows.Add(100, "Apples", "Fruit"); dt.Rows.Add(101, "Pears", "Fruit"); dt.Rows.Add(102, "Carrots", "Vegetables"); dt.Rows.Add(103, "Beef", "Meat"); dt.Rows.Add(104, "Cucumbers", "Vegetables"); dt.Rows.Add(DBNull.Value, "(other)", DBNull.Value); dt.Rows.Add(105, "Chillies", "Vegetables"); dt.Rows.Add(106, "Strawberries", "Fruit"); groupedCombo.DataSource = dt.DefaultView;
String Array (grouped by word length)
groupedCombo.ValueMember = null; groupedCombo.DisplayMember = null; groupedCombo.GroupMember = "Length"; string[] strings = new string[] { "Word", "Ace", "Book", "Dice", "Taste", "Two" }; groupedCombo.DataSource = strings;
Final Words
This custom control is an example in enhancing GUI usability and correctly using both the Windows Forms data binding infrastructure and visual style guidelines. With the proper knowledge of how these features operate under-the-hood, I was able to create a component that recycles as much existing functionality as possible, is simple and easy to use in code and avoids breaks with convention that usually result in complex, clunky solutions. Of course, it does not give the ComboBox control a new, multi-level hierarchical structure, merely a categorisation mechanism – however, in this capacity, I hope you find it useful!
Download
Please visit the project page here: Drop-Down Controls
Pingback: C#/Windows Forms: How to get DrawMode to become DrawMode.OwnerDrawVariable | trouble86.com
Hi Brad,
Awesome control!
I’m trying to convert this into a CheckedListBox but seems I can’t get DrawMode to change from Normal to OwnerDrawVariable so OnMeasueItems is never entered.
http://stackoverflow.com/questions/8117603/c-windows-forms-how-to-get-drawmode-to-become-drawmode-ownerdrawvariable
Can you help?
Regards
/Niels
Unfortunately, the CheckedListBox control does not support owner-drawing. You might be better off using a TreeView control with the CheckBoxes option enabled.
Pingback: How to get DrawMode to become DrawMode.OwnerDrawVariable
Brad,
Very nice control thanks!
Mike
Hi Brad,
I just want to say thank for ths great control. It saved me a whole lot of time and effort.
Thanks again,
Ross
Pingback: Drop-Down Controls Revisited | Brad Smith's Coding Blog
Thanks a lot for the nice control. I used in my windows form app. it generally works great. But I got some issue:
I shared this control between two views depending on the user selected view. then i dynamically bind the corresponding data source, let’s say, one view have 4 items, the other one has 6. If i start from 4 items view, then switch to 6 items view, the control works perfectly; but if i start from 6 item view (if i select the item 5 or 6), then switch to 4 items view, then, the control works but with a display issue, now it will not just display the selected value, but it displays something like this { Value=5, Display=”New Photo”, Group=”03-Add Record” }, which is the raw arraylist item.
so in short, the issue is if i switch from shorter list to a longer list, it works fine with no issue, but if i switch from longer list to shorter list, it works, but got the display issue.
Could you please see if you can direct me on the possible fix for this issue.
Thanks a lot.
Regards.
added the above to your constructor GroupedComboBox fixed my issue
public GroupedComboBox() {
base.DropDownStyle = ComboBoxStyle.DropDownList; ////// added this line fixed my issue
base.DrawMode = DrawMode.OwnerDrawVariable;
mGroupMember = String.Empty;
mInternalItems = new ArrayList();
mTextFormatFlags = TextFormatFlags.EndEllipsis | TextFormatFlags.NoPrefix | TextFormatFlags.SingleLine | TextFormatFlags.VerticalCenter;
}
I’m not quite sure why that would fix the issue; i’ll look into it and see if I can find an answer.
I found a solution .. You must first set the datasource and then set the valuemember displaymember and groupmember .. that works for me..
groupedComboBox1.DataSource = new ArrayList(carreras);
groupedComboBox1.ValueMember = “Value”;
groupedComboBox1.DisplayMember = “Display”;
groupedComboBox1.GroupMember = “Group”;
I was curious if it is possible to make the Group items selectable?
Here is my ideal solution:
class GCBItem
{
public String Display { get; set; }
public String Group { get; set; }
public bool IsGroup { get; set; }
public object Value { get; set; }
}
gcb.Items.Add(new GCBItem(“Group 1”, “G1”, true, “G1”));
gcb.Items.Add(new GCBItem(“Item 1”, “G1”, false, “G1_I1”));
gcb.Items.Add(new GCBItem(“Item 2”, “G1”, false, “G1_I2”));
gcb.Items.Add(new GCBItem(“Group 2”, “G2”, true, “G2”));
gcb.Items.Add(new GCBItem(“Item 1”, “G2”, false, “G2_I1”));
gcb.Items.Add(new GCBItem(“Item 2”, “G2”, false, “G2_I2”));
gcb.DisplayMember = “Display”;
gcb.GroupMember = “Group”;
gcb.IsGroupMember = “IsGroup”;
gcb.ValueMember = “Value”;
gcb.DataSource = gcb.Items;
^ Basically, I would like the current logic as you have it, but if you set a .IsGroupMember value, it uses that to determine if an item is a group item, and what items should go into it, plus it allows the user to select the “Group 1” item.
Disregard. I figured out how to modify your code to fit my needs. Thanks for a great starting point!
You might also be interested in looking at another control I developed; 🙂
Hey Brad, Good job!
Just one thing where I was ripping my hair out:
When you set the datasource property with your data and then set it again with a different data source a second time, this does not refresh the items in the list. Instead you have to first set the datasource to null and then to the desired list.
e.g.
this.cmb_groupedItems.DataSource = null;
this.cmb_groupedItems.DataSource = myItems;
Thanks!
Thanks for spotting this – i’ll soon be releasing an updated version that does not suffer from this glitch.
Problem!
The example code is not run!!. The next code not display elements in combo Box:
GroupedComboBox groupedCombo = new GroupedComboBox();
groupedCombo.ValueMember = “Value”;
groupedCombo.DisplayMember = “Display”;
groupedCombo.GroupMember = “Group”;
groupedCombo.DataSource = new ArrayList(new object[] {
new { Value=100, Display=”Apples”, Group=”Fruit” },
new { Value=101, Display=”Pears”, Group=”Fruit” },
new { Value=102, Display=”Carrots”, Group=”Vegetables” },
new { Value=103, Display=”Beef”, Group=”Meat” },
new { Value=104, Display=”Cucumbers”, Group=”Vegetables” },
new { Value=0, Display=”(other)”, Group=String.Empty },
new { Value=105, Display=”Chillies”, Group=”Vegetables” },
new { Value=106, Display=”Strawberries”, Group=”Fruit” }
});
Why occurs this to me?.
Thanks for you response.
Did you remember to add the control to a form? The updated code contains a working example application.
Yes, I have included the control to form (your name is .group_combo_box_produt) !, this control appear in it correctly but your contain is empty.
When I insert items to combo Box I write:
this.group_combo_box_produts = new GroupedComboBox();
Next I test the source code that You write in this page, for example:
group_combo_box_produts.ValueMember = null;
group_combo_box_produts.DisplayMember = null;
group_combo_box_produts.GroupMember = “Length”;
string[] strings = new string[] { “Word”, “Ace”, “Book”, “Dice”, “Taste”, “Two” };
group_combo_box_produts.DataSource = strings;
My comboBox is empty!!!!!!. Not load items!.
If I execute another example nothing appear.
Thanks for you response.
Note: In the page “updated code” not conains a working example application.
My version of .NET is 4.5.50709 and I use Visual Studio 2012.
It looks like you’re re-instantiating the control unnecessarily. Try removing the line:
this.group_combo_box_produts = new GroupedComboBox();
and see if that resolves the problem.
Ok!! you were right, the problem is solved. Thank you.
You are a master!!!!!!
If I want do the same example but with ComboTreeBox, how is it?
I would encourage you to read the original article for more information and examples. The main difference is that the ComboTreeBox uses nodes as its data representation and does not support data-binding.
Hi,
Can you please tell me how to reverse the order of grouping member elements? The grouping member elements are in alphabetical order by default.
In the Compare(x,y) method, multiply the value of ‘firstLevelSort’ by -1 before the method returns; that will reverse the order of the grouping elements but retain the order of the child elements. If you wanted to be able to control this behaviour dynamically, you could add a SortDirection (ascending/descending) property to the control – which would multiply the value by either +1 or -1.
Hello,
thank you for the great solution.
1. How could i modify the sorting to insert an item like “Create…” on index 0 before the first group?
2. How can i remove the empty space before the items
Thanks for your support!
Re: Forcing an item to always be at index 0, there are a couple of ways to achieve this, but I think this is the simplest way:
Firstly, set the SortMode property to GroupItemSortModes.Item – this passes the entire combo box item to the sort comparer.
Then, create a custom IComparer implementation and set the SortComparer property.
In your comparer class, you will need to test for some condition that is true for the “Create…” item only. In the example below, I assign ‘SOME_VALUE’ to the Value property of that item:
I’m not sure what you mean by empty space before the items – do you mean the left indent? If so, this is hard-coded into the control’s Paint method. You would have to override OnDrawItem in order to change this.
Hello,
thanks for your answer.
I have to extend my question. I want to insert an item like “Create / Edit…” at the top or at the end of the comboBox. You can see it often in dropdowns on a website form.
What i’ve tried to have it at the top:
I used a “Group” value like “zzz” to have it on index 0 and tried to remove the header (Groupname) by defining “e.ItemHeight” in the “OnMearsureItem” method. Unfortunately it doesn’t work properly. The item should be above the first group.
– I’ve realized that i have to deactivate the group header sorting because i wants to translate my application to a few languages for usability.
– I also wants to deactivate the sorting in the first group after the “Create” item because it should have predefined settings.
Example:
“Create” item [first group] -> Predefined Settings [2nd group] -> Userdefined Settings [3rd group]
Yes, i guess the left indent is defined in the “OnDrawItem” method by itemBounds.
– I found a very elegant dropdown on the web with lines between the groups. I think this would be also a nice extension worth considering for the combobox.
(Example: https://www.jotform.com/widgets/grouped-values-dropdown)
Was trying to use this control as a custom comboboxcell column in a datagridview, although it sort of works the grouping doesn’t. I don’t mind fixing, however, could you give me some pointers as to why this might be the case?
Two key things are needed in order to get the grouping behaviour to work;
1) DrawMode must be set to OwnerDrawVariable. Make sure the DataGridView isn’t changing this property on the editing control.
2) The GroupMember property must be set on the editing control. If you’ve added an equivalent property to the cell/column, be sure to apply its value to the editing control. You can do this in the DataGridViewCell.InitializeEditingControl method (of the cell) or the IDataGridViewEditingControl.PrepareEditingControlForEdit (of the editing control).
Hi,first thanks for your good control,it’s great
i have a question,how can i change the COLOR of subItem in group of ComboBox?
thanks for your help
with best regards
In this implementation, the colour for all of the drop-down items in the GroupedComboBox is determined by the control’s ForeColor property. If you want to be able to set the colour of each item individually, you would have to add an additional databinding property to the control (using GroupMember as an example), and add an appropriate member to the items in the data source that contains the colour you want to use. This would be a highly-specialised solution, however, and at this point I would suggest using a more appropriate type of control to display your items. This sort of requirement goes beyond what databinding in Windows Forms was designed for.
You may want to try a control with a hierarchical data model, such as my ComboTreeBox. It would be much easier to implement individual node colours using this control than the GroupedComboBox.
Hi,
And thanks for this wonderful example, looks pretty good. I wanted to ask you if you had done the same kind of work for a listbox or listview instead?
Angelo
These principles can be applied to the ListBox control very easily, as they use the same mechanism of owner-drawing as the ComboBox. I might adapt my code at some point in the future.
As for the ListView control, it already has a grouping mechanism (albeit a cumbersome one). Due to the lack of support for data-binding, this control is not ideal.
I may well give it a whirl and see if I can simply adapt the code to the listbox, if they use the events then I suoppose I could clone the class you created and simply switch from combobox/dropdownlist to listbox …
Hi, how can i add this group combobox feature to DataGridView?
Mind giving me some pointer? Thanks.
Here is a useful tutorial for creating custom DataGridView cell and column types: Build a Custom NumericUpDown Cell and Column for the DataGridView Control. The article uses a NumericUpDown control, but you could easily substitute my GroupedComboBox control.
I do plan to eventually include custom cell and column types in my DropDown Controls project.
I was just wondering how far away this implementation would be, as being able to use the grouping option in a DataGridView would be very useful. As i’m still new at C# i’m not sure where to start to do this myself
Your wish is my command: DataGridView Column Types for Drop-Down Controls 🙂
Pingback: DataGridView Column Types for Drop-Down Controls | Brad Smith's Coding Blog
I like groupedcombobox, can please tell how make auto complete only to selectable.
Regards,
Ram
Since the GroupedComboBox control extends the existing ComboBox control, you can achieve this by simply setting the AutoCompleteMode property to Suggest (or Append, or SuggestAppend as desired) and the AutoCompleteSource property to ListItems. Only the items themselves will appear in the auto-complete suggestions; the groups will only appear in the main drop-down.
Hi Brad,
Thank you so much for this!
Is there a way to disable the combobox from auto-selecting an item in the dropdown until I click on one?
Thank you
Hi Luke, this is the default behaviour for all ComboBox controls that use data-binding (on Windows Vista and later). The easiest way to get around this is to set the SelectedItem property to null immediately after setting the data source (or adding items).
Hi,
thank you for your control.
Did you know or do you have a solution why no list is shown when I only have one Item?
For example when you do this:
groupedCombo.ValueMember = null;
groupedCombo.DisplayMember = null;
groupedCombo.GroupMember = “Length”;
string[] strings = new string[] { “Word” };
groupedCombo.DataSource = strings;
No list will open when you click the drop down button???
Regards
Martin
Hi Martin, I can reproduce your problem but, at this stage, I cannot explain why it is happening. The control is returning the correct item height in the MeasureItem event, which should result in a dropdown with a height equal to twice the font size. It’s possible that there could be a bug in the ComboBox control when in OwnerDrawVariable mode. I will have to test this more extensively.
Thank You for your reply, yes I also was looking into the code and was looking into the function OnMeasureItem and can’t find any failure. Thats the reason why I asked you 🙂
Thank You if You will test more in this failure.
Regards
Martin
Hi,
its me again 🙂
I test a little bit and found a solution.
If you set groupedCombo.ItemHeight = Font.Height * 2 it is working but than the hole combobox has this height.
If you set
groupedCombo.IntegralHeight = False;
groupedCombo.DropDownHeight = 200;
Than it is working. Maybe this helps others 😉
Thank You!
Hi,
In this dropdown i think it is automatically sorting the items…How can i avoid it and sort from my query…. Is it possible?
Yes, it is necessary for the control to sort the items using custom logic because that is how the grouping is achieved. To change this behaviour you would have to modify the IComparer.Compare method and replace the default comparer (for ‘secondLevelSort’) with your own implementation.
Hello , first congratulations on the component.
I use vb.net and am trying to use your component.
My syntax to populate a combobox component is :
Dim Conexao As New OleDb.OleDbConnection(CONNECTION_STRING)
Dim ConsultaSQL As String = ""
Conexao.Open()
Dim SQLComando As New OleDb.OleDbCommand(ConsultaSQL, Conexao)
Dim Ler As OleDbDataReader
Dim SQLTexto As String = ""
If Texto.Trim "" Then
SQLTexto = " WHERE uCase(titulo) like '%" & Texto.ToUpper & "%' or uCase(localizacao) like '%" & Texto.ToUpper & "%' or endereco like '%" & Texto & "%' "
End If
ConsultaSQL = "SELECT * FROM equipamentos " & SQLTexto & " ORDER BY grupo ASC,titulo ASC"
Dim SQLCommand As New OleDb.OleDbCommand(ConsultaSQL, Conexao)
Ler = SQLCommand.ExecuteReader()
cmbTextoPesquisa.Items.Clear()
cmbTextoPesquisa.Items.Add(New ComboTagLista("", ""))
Dim nCont As Integer = 0
Dim GrupoAux As String = ""
Dim Dispositivos As New AutoCompleteStringCollection
While Ler.Read
Dim ID As String = Ler.Item("ID").ToString
Dim Titulo As String = Ler.Item("Titulo").ToString
Dim Tipo As String = Ler.Item("tipo").ToString
Dim IDGrupo As String = Ler.Item("grupo").ToString
Dim Grupo As String = NomeGrupo(Ler.Item("grupo").ToString)
If GrupoAux Grupo Then
cmbTextoPesquisa.Items.Add(New ComboTagLista("_____________________________________", ""))
cmbTextoPesquisa.Items.Add(New ComboTagLista("", "CONEXOES#" & IDGrupo))
GrupoAux = Grupo
End If
cmbTextoPesquisa.Items.Add(New ComboTagLista(Titulo, Tipo.ToUpper & "#" & ID))
Dispositivos.Add(Titulo)
nCont += 1
End While
cmbTextoPesquisa.AutoCompleteMode = AutoCompleteMode.Suggest
cmbTextoPesquisa.AutoCompleteSource = AutoCompleteSource.CustomSource
cmbTextoPesquisa.AutoCompleteCustomSource = Dispositivos
Ler.Close()
Conexao.Close()
But I can not adapt to your component , any suggestions?
Hi, you will not get the grouping functionality if you add items directly to the Items property of the control; you must use the DataSource property and set the DisplayMember, ValueMember and GroupMember properties accordingly. Also, grouping will not appear in the autocomplete popup.
Hi, I hope you will read my message, this post is 2 years old…
btw, I would like to use the GroupedComboBox.cs in one of my vb.net projects.
How can use it?
Do I need to translate GroupedComboBox.cs to VB ?
Thanks in advance
Here again, just sort…I didn’t see the DLL …In order to use this great combo in vb.net, I think the only way is using the DLL. Isn’t it?
Now I have my combo box with groups…thanks to the author for sharing his good control.
Yes, that’s correct. There’s no need to port the code to VB.NET, you can just use the pre-built assembly.
Ok!
Thanks for the feedback, I will do more tests using DataSource property and set the DisplayMember, ValueMember and GroupMember properties.
And congratulations on component development.
Hello,
What is the licence used by your control?
Bye
Licence information can be found here as well as in the LICENSE.TXT file included in the source and binary distributions.
hi thanks for this component. the grouComboBox is exactly what i needed. it it noticeably sluggish compared to a combobox with about 30 items in the dataset though. is there anything i can do to improve this? as long as the items are grouped correctly i do not care about order.
I can’t say that i’ve observed any sluggishness under normal operation, even with 100+ items in the dropdown. Is the data source changing often? The control rebuilds its internal list of items whenever a change to the data source is detected, so this could account for the behaviour you described. Unfortunately, the items must be sorted in order for the grouping to be performed correctly so this is a necessary overhead. I’m sure it could be optimised for a specific application, but the data source should not really be changing frequently enough to cause a noticeable slowdown.
Another possible explanation is that the property referenced by GroupMember does not exist or is throwing exceptions (although this would mean that the grouping was already failing).
Is their away to make the GroupedComboBox not Sort alphabetically the data i bind to it ?
At minimum, the collection must be sorted by group. By default, the control sorts first by group and then by item, using the default comparer. If you can guarantee that the items in the collection are already sorted by group, you can write your own IComparer implementation and assign it to the SortComparer property of the control. Rather than comparing the items, it can simply compare their indices.
Is this Possible with textbox control with autocomplete source
The AutoComplete dropdown is provided by the operating system, so there’s no way to control how it’s displayed. Have a look at my searchable drop-down control which does something similar.