Monday, June 16, 2008

Enhanced combobox with readonly and working value member

As I had several problems with the standard combo box of the .Net 2.0 Framework such as the missing read only property or the not working value member property (if not using a datasource), I decided to write my own improved combo box. To change the default combobox to my needs I overwrote the OnPaint() mehtod and provided some additional methods.

With the enhanced combo box you can finally specify a value member without having to use a datasource as you can see in the code below:
Customer customer1 = new Customer();
customer1.FirstName = "John";
customer1.ID = 1;
customer1.LastName = "Smith";

Customer customer2 = new Customer();
customer2.FirstName = "Marco";
customer2.ID = 2;
customer2.LastName = "Polo";

this.cmbTest.Items.Add(customer1);
this.cmbTest.Items.Add(customer2);

this.cmbTest.DisplayMember = "FirstName";
this.cmbTest.ValueMember = "ID";

Or you can use a datasource:
Customer customer1 = new Customer();
customer1.FirstName = "John";
customer1.ID = 1;
customer1.LastName = "Smith";

Customer customer2 = new Customer();
customer2.FirstName = "Marco";
customer2.ID = 2;
customer2.LastName = "Polo";

Customer customer3 = new Customer();
customer3.FirstName = "Tom";
customer3.ID = 3;
customer3.LastName = "Hanks";

List customers = new List();
customers.Add(customer1);
customers.Add(customer2);
customers.Add(customer3);

this.cmbTest.DataSource = objects;

this.cmbTest.DisplayMember = "FullName";
this.cmbTest.ValueMember = "ID";

Additionally you can register for different object types a display and value member. This gives you a lot of flexibility when you have to show information of different objects in the same combobox.
To register a new type call the "RegisterType" method and pass it the type of the object, the name of the property which is used as display member and the name of the property which is used as value member.
this.cmbTest.RegisterType(typeof(Customer), "FirstName", "PK");

To unregister a registered type call "UnregisterType" and pass it the type you want to unregister.
this.cmbTest.UnregisterType(typeof(Customer));

To modify the display and/or value member of a registered type call "ChangeValueMember" or "ChangeDisplayMember" respectively and pass it the new name of the property you want to use.
this.cmbTest.ChangeDisplayMember(typeof(Customer), "FullName");
this.cmbTest.ChangeValueMember(typeof(Customer), "LastName");
Of course all this operations can be performed at runtime and the changes are immediately visible in the combo.
To obtain the value of the selected item, simply call "SelectedValue".
Console.WriteLine(this.cmbTest.SelectedValue.ToString());
To get the selected object simply call "SelectedItem" and cast it to the correct type.
Customer selectedCustomer = this.cmbTest.SelectedItem as Customer;
if (selectedCustomer != null)
{
...
}
NOTE: If the combobox has to visualize a type which is not registered it calls the ToString() method of the object and uses that string.

Now I'd like to show you the read only property. The enhanced combobox provides a read only property which disables the drop down button and changes the back color of the combobox to "Control". The read only property is not a real read only because it is not possible to select and then copy the visible text. The most important point is that you can change the fore- and backcolor of the combobox when it is in read only because it is not very easy to read the text of a disabled combo box. The images below show what I mean.

This is a disabled ComboBox:


This is a read only ComboBox:


You can set any color as read only back- and fore color you like:



That's all about my enhanced Combo Box. I hope it helps someone to achieve what he wants. If you have suggestions on how to improve it or you found a bug, feel free to leave a comment.

You can download the DLL from here and simply drag and drop it to your visual studio toolbox.
Or you can get the source code with a sample tool which shows how to use it from here.

8 comments:

James said...

Does your implementation let you use an integer for the value member?

The default implementation only lets you use a string which is useless when you have a list of primary keys.

Manfred said...

Hello James,
I don't really get what you mean with "use an integer for the value member"? The value member is a string which is the name of the property that is used to retrieve the value. The property can have any type (e.g. string, int, long, bool,...), because if you call enhancedCombo.SelectedValue you get an object of type object which can be anything.
If you mean something different please let me know...

James said...

Hi Manfred,

I am binding the datasource property of the comboBox to a System.Data.DataTable, I am then setting the DisplayMember to a column name that I want to use as the Display variable and setting the ValueMember to the primary key column. The comboBox does bind to the DisplayMember column perfectly but when it tries to bind to the ValueMember column I get exception claiming it can not bind to a variable of that type (bigint in the database equivalent to a 64 bit signed integer) hence why I asked the question about integers. I have since come to the conclusion that binding to a DataTable is not supported properly by a ComboBox and it only worked partially by chance. Here is the code:

this.comboBox_MediaOwners.DataSource = m_mediaOwners;
this.comboBox_MediaOwners.DisplayMember = "CompanyName";
this.comboBox_MediaOwners.ValueMember = "ComapanyID";

Any thoughts on this would be appreciated.

James

Manfred said...

Hello James,
I have added now support for a DataTable as datasource. I tested it only with a DataTable created by code and not by getting one from a database, but that should not matter. I tried it and it worked. If you find some problems or other issues please let me know.
I have also corrected some graphical problem which regards hovering with the mouse pointer.

You can get the new dll from the known link. The source code is now a Visual Studio 2008 solution, because I don't have installed VS2003 anymore. I hope this helps you.

Manfred said...

I misstyped the version of Visual Studio. I clearly ment VS2005 at the place of VS2003.

John said...

Manfred can you post a sample on changing the value of a number in a combo box. For instance I want the number 6 in my combo box to have a diffrent value when I do a function in my code

Thank you John

Manfred said...

Hi John,
if I understood You correctly then you want to display "6" in the combo but when accessing the selected value You want to have something different?

Well consider this example:
this.enhancedComboBox2.RegisterType(typeof(KeyValuePair), "Key", "Value");
this.enhancedComboBox2.DisplayMember = "Key";
this.enhancedComboBox2.ValueMember = "Value";
this.enhancedComboBox2.Items.Add(new KeyValuePair(6, "six"));
this.enhancedComboBox2.Items.Add(new KeyValuePair(7, "seven"));

And in the function you can use:
string selectedNumber = (string)this.enhancedComboBox2.SelectedValue;

Hope this helps

John said...

Hi Manfred
I want to thank You for helping me on combo box value members.

I now need help on another project
1--I built a windows over data base record keeping program.I need to build into the program a backup/Restore windows form, I know that my file to back up is a .sdf file, but when I publish my program I cannot find the file, there is just 1 folder and it called a clickonce ApplicationReference. Tell me where are my other files are or didnt I publish it the right way. If I could find a sample that has a backup/Restore form in it it (maybe) will help.

Would you have any other Ideas??

Thank You