Using an Enum Class in a DataGridView Combobox Column
Objective
The purpose of this article is to provide a working example of an enum class column on a DataGridView on a Windows Form using C++/CLI where data is drawn from multiple datasources via DataTables. It will also show a simplified approach to the same feature drawing data from a single source without using a DataTable and some other unrelated features:
- Row banding
- Cell highlighting
- Error Icons in individual cells
- Using an
enumclass as a flag set - Building a
DataGridViewfrom multiple data sources
Background
There are very few examples of enum class columns on the DataGridView for C++/CLI or indeed C# returned when searching the web, but plenty of requests for assistance. The examples that are present populate their grids from a single datasource and are both simple and effective in their execution. However, my attempts to broaden this approach to a DataGridView drawing its data from two or more data sources led to persistent encounters with the display errors: “Error Happened: Formatting, Display” and “Error Happened: Formatting, Preferred Size”. This is down to a conflict between the natural state of an enum class entry which is ‘Int’ and the natural state of a DataGridView cell which is ‘String’, and it’s not as simple as a quick cast.
Example Origin
The example is drawn from a system I am working on, which relies heavily on expiry dates. Items can expire by a number of time units added to the current date, e.g., two weeks from Today, or by ‘Fixed’ units where by the time unit ends on a specified day regardless of commencement, e.g., Friday of next week. For simplicity, the example has a hardcoded representation of the ExpiryMaster table as its primary datasource and an extract of the Units hardcoded as its secondary datasource. Fixed intervals have been omitted because they do not add any additional value to the example. I have chosen to hardcode the datasources because I presume I am not alone in downloading CodeProject examples only to find I do not have the $%^*!%$ (sorry, required) database installed.
Presenting Data from a Single DataSource
This is the stuff of a Tech Tip, such is its simplicity. There are essentially only two key extra lines involved.
- Create a new Windows Form Application
- Put a new
DataGridViewon the Form - Add two Columns to the form, a text column ‘
dgName’ and a combobox column ‘dgStatus’ - Define your
enumclass before the definition of theForm1class:
public enum class Status_Type {Married, Single};
Add these two lines to the Form1 Constructor (or load event):
dgStatus->ValueType = Status_Type::typeid;
dgStatus->DataSource = Enum::GetValues(Status_Type::typeid);
Compile and run the application, and there you have it, a working enum class column in a DataGridView.
Here's some data to load in:
dataGridView1->Rows->Clear();
array<Object^>^ itemRec = gcnew array<Object^> {"Sean",Status_Type::Married};
dataGridView1->Rows->Add(itemRec);
array<Object^>^ itemRec2 = gcnew array<Object^> {"Tom",Status_Type::Single};
dataGridView1->Rows->Add(itemRec2);
And this works well while data is added row by row through the Rows?Add method. Hardly this stuff of legend. The code for this is in the attached DataGridView1 example.

The fun starts when the DataGridView is mapped to a DataTable!
The Enum Class Column on a DataGridView that Draws Data from Multiple Sources using DataTables
When populating your DataGridView using a DataTable, the secret to a successful enum class based column is to map that enum class to a DataTable of its own, and treat it as any other secondary datasource.
Our Objective
This is what we are aiming for:
Architecturally, the primary DataTable is driven by the ExpiryMaster DataSource, and there are secondary DataTables for the Expiry Type enum class and another for the Unit DataSource. The DataTable derived from the Unit DataSource drives two combobox columns for good measure, allowing your user to choose a Unit by ID or by Description with the other value updated to correspond.
The Ingredients for a Successful Enum
The Enum class can be defined immediately before the Form class:
public enum class Expiry_Type {Units, Fixed_Date};
(although in my working system, I keep definitions like this in a shared assembly).
Include these definitions in your Form class to enable mapping of the enum class to a DataTable:
DataSet ^dsExpTyp;
BindingSource ^bsExpTyp;
DataTable ^dtExpTyp;
I have included a temporary variable to interrogate new values coming from the datagrid:
Expiry_Type empExpType;
You may position them using the IDE or by referencing my attached example (DataGridEnum4).
As part of my LaunchForm function called from my form constructor, I have:
A definition for the DataTable that will hold the Enum class:
dtExpTyp = gcnew DataTable("dtExpTyp");
dtExpTyp->Columns->Add("Expiry_Type", Int32::typeid);
dtExpTyp->Columns->Add("Expiry_Type_Desc", String::typeid);
bsExpTyp = gcnew BindingSource();
dsExpTyp = gcnew DataSet();
dsExpTyp->Tables->Add(dtExpTyp);
The DataTable that maps to the DataGridView needs this entry to map to the above datastructure:
dtViewExpiryData->Columns->Add("Expiry_Type", Int32::typeid);
The datasource for the column is defined as follows:
gridExpiry->Columns[dgExpiry_Type->Index]->DataPropertyName = "Expiry_Type";
Note: In this example, the DataGridView is called ‘gridExpiry’.
And a call to a function that will do the mapping:
Load_Expiry_Enum();
You can see their placement in the example.
The Load_Expiry_Enum() source code is:
{
DataRow ^row;
for each (Expiry_Type^ tmpEx in Enum::GetValues(Expiry_Type::typeid))
{
row = dsExpTyp->Tables["dtExpTyp"]->NewRow();
row["Expiry_Type"] = tmpEx;
row["Expiry_Type_Desc"] = tmpEx->ToString();
dsExpTyp->Tables["dtExpTyp"]->Rows->Add(row);
}
// Set up the UnitDesc binding source
bsExpTyp->DataSource = dsExpTyp;
bsExpTyp->DataMember = "dtExpTyp";
// bind Name
dgExpiry_Type->DataSource = bsExpTyp;
dgExpiry_Type->DisplayMember = "Expiry_Type_Desc";
dgExpiry_Type->ValueMember = "Expiry_Type";
}
The section that follows this one will show you how this fits in to the primary datasource in the same manner any other secondary datasource. In the meantime, we will conclude this section by looking at the code to fetch, display, and interrogate the data on the enum class column on the DataGridView.
Now, we will look at two ways of getting the data from the enum column.
try
{
if (gridExpiry->Rows[e->RowIndex]->Cells[dgExpiry_Type->Index]->Value != nullptr
&&gridExpiry->Rows[e->RowIndex]->Cells[dgExpiry_Type->Index]->
Value->ToString() != "")
{
String^ IntExpType = gridExpiry->Rows[e->RowIndex]->
Cells[dgExpiry_Type->Index]->Value->ToString();
empExpType = safe_cast<Expiry_Type>
(System::Convert::ToInt32(IntExpType));
lblExpiryType->Text = empExpType.ToString();
}
}
catch (...)
{
lblExpiryType->Text = nullptr;
}
And:
array<DataRow^>^ row;
try
{
if (gridExpiry->Rows[e->RowIndex]->Cells[dgExpiry_Type->Index]->Value != nullptr
&&gridExpiry->Rows[e->RowIndex]->Cells[dgExpiry_Type->Index]->
Value->ToString() != "")
{
String^ IntExpType = gridExpiry->Rows[e->RowIndex]->
Cells[dgExpiry_Type->Index]->Value->ToString();
row =dtExpTyp->Select(String::Format("Expiry_Type={0}", IntExpType));
if (row->Length > 0)
{
// ItemArray[0] holds the integer representation of the enum,
// alternately
lblExpiryType->Text = row[0]->ItemArray[1]->ToString();
}
}
}
catch (Exception ^e)
{
String ^MessageString =
" Error reading internal enum table for Expiry Type: " + e->Message;
MessageBox::Show(MessageString);
lblExpiryType->Text = nullptr;
}
Either one of these methods allows you to use your enum class at will.
Walking Through the Attached Code
The attached DataGridEnum4 example is a complete Visual Studio 2008 project, cleaned before zipping. First a note on my style. I don’t like much more than the definitions in my .h files, so where the IDE adds a Windows Form function to the .h, my practice is to call a user defined function in the .cpp passing on the parameters. It’s an overhead, because I cannot force the IDE to insert Windows Form functions directly into the .cpp, and when I relocate them there, the IDE gets confused – but I prefer the order that it gives things.
This sample is a standard Windows Forms application with a DataGridView and several labels dropped on from the toolbox as illustrated in the second screenshot. The Expiry Type column is a combobox column as are Unit ID and Unit Description, the other three columns are textbox columns. All this is achieved using the form designer.
In here, we will take a further look at the Form1.h and DataGridEnum4.cpp modules.
Form1.h
This is the complete list of assemblies I am referencing:
using namespace System;
using namespace System::ComponentModel;
using namespace System::Collections;
using namespace System::Windows::Forms;
using namespace System::Data;
using namespace System::Drawing;
using namespace System::IO;
using namespace System::Collections::Generic;
I have defined the Expiry_Type enum class inside the application namespace, but before the form class.
In addition to the Launchform function call, I have one other line in the constructor. This is for the error icon.
m_CellInError = gcnew Point(-2, -2);
After the...
#pragma endregion
...I define my internal variables and functions. Included here is another enum class, this one prefixed [Flags]. You will see examples of a flag variable, m_ElementList, being set and reset throughout the code. This is particularly useful when you need to write an update statement to commit your changes to a database. The flag settings determine the columns that need updating.
There are two variables defined for the error icon handling followed by the definitions for the DataTables, BindingSources and DataSets.
The final segment of the .h file has the function definitions added through the form designer on the IDE for the DataGridView and the Exit button. They are:
UserAddedRow(not used in this example)CellValueChangedCellValidating(not used in this example)ColumnHeaderMouseClick– used to eliminate double click on the dropdownsRowEnterRowValidatingCellBeginEdit– Stores colours – doesn’t have anything in the .CPPCellEndEdit– Restores Colour, then calls .CPP functionExit_Click– Shuts down the exampleCellClickDataError
DataGridEnum4.cpp
LaunchForm
There are a few interesting ‘bells and whistles’ on show in this function called from the constructor.
This code customized the grid header:
DataGridViewCellStyle^ headerStyle = gcnew DataGridViewCellStyle;
headerStyle->Font = gcnew System::Drawing::Font("Times New Roman", 12,FontStyle::Bold);
gridExpiry->ColumnHeadersDefaultCellStyle = headerStyle;
Personalizing the selection colours:
gridExpiry->DefaultCellStyle->SelectionBackColor=Color::FromArgb(255,255,128);
gridExpiry->DefaultCellStyle->SelectionForeColor=Color::Black;
Tooltip text on the column headers:
for each(DataGridViewColumn^ column in gridExpiry->Columns)
column->ToolTipText = L"Click to\nsort rows";
Apply Colour banding to the grid:
gridExpiry->AlternatingRowsDefaultCellStyle->BackColor = Color::LightGray;
Datatable for the Units datasource and the Expiry Type Enum class are defined as seen already, followed by the datatable definition for the expiry datasource.
Next up, the grid columns are mapped to data properties:
gridExpiry->Columns[dgExpiry_ID->Index]->DataPropertyName = "Expiry_ID";
gridExpiry->Columns[dgExpiry_Type->Index]->DataPropertyName = "Expiry_Type";
gridExpiry->Columns[dgDescription->Index]->DataPropertyName = "Description";
gridExpiry->Columns[dgUnitIDNum->Index]->DataPropertyName = "UnitIDNum";
gridExpiry->Columns[dgUnitDesc->Index]->DataPropertyName = "Unit_ID";
gridExpiry->Columns[Number_Interval_Units->Index]->
DataPropertyName = "Number_Interval_Units";
The function concludes with calls to dedicated functions which will populate each data table and conclude the grid definition.
List_Setup
The key thing here is the binding of the primary datatable to the DataGridView:
gridExpiry->DataSource = dtViewExpiryData;
RowEntered
This function has little of note except the initialization of the flags, and reading the data from the combo box columns. I have already given two examples of how to read the comb box columns, here are the Enum Flags being initialized:
m_ElementList = m_ElementList & ~ m_FlagBits::EXPIRY_ID;
m_ElementList = m_ElementList & ~ m_FlagBits::EXPIRY_TYPE;
m_ElementList = m_ElementList & ~ m_FlagBits::DESCRIPTION;
m_ElementList = m_ElementList & ~ m_FlagBits::UNIT_ID;
m_ElementList = m_ElementList & ~ m_FlagBits::NUMBER_OF_INTERVAL_UNITS;
CellEndEdit
Use this function to display an icon in any cells that you want to enforce population of. In this example, I am applying it at row level to produce this effect:
CellValueChanged
Again, you have already seen how a combobox column may be read. The other feature of note here is the setting of a flag when a cell value change is detected.
m_ElementList = m_ElementList | m_FlagBits::EXPIRY_ID;
CellClick
ComboBox columns on DataGridViews have an annoying habit of needing to be clicked twice in order to open the dropdown. This function has the effect of opening the dropdown as soon as the cell is clicked.
RowValidating
This function places the error icon on any cell that should have a value when your user attempts to leave a row.
DataError
This section is included to handle unforeseen display failures on the DataGridView. These errors occur when the DataGridView doesn’t know how to display a particular value. For example, in this solution where we are using datatables, if we used the simpler enum method above, we would see plenty of “Error Happened: Formatting Display” etc., because the DataGridView is looking for a string value, but the enum class is supplying an integer.
That said, it is good practice to include this function regardless of whether or not you are using enum classes because it nicely catches unexpected display issues with the DataGridView.
History
- 2011-05-16 - V1.0 - Initial submission
发表评论
Zu9Ooz to find something more safe. Do you have any suggestions?
5FJYxH Websites we recommend Wow, amazing blog layout! How long have you been blogging for? you make blogging look easy. The overall look of your site is fantastic, as well as the content!
hCr2Db There is certainly a great deal to know about this subject. I love all of the points you have made.
qdDuDK Muchos Gracias for your article.Much thanks again. Keep writing.
ICUnQP Looking forward to reading more. Great article post.Much thanks again. Really Great.
FnUmGU Very neat post.Much thanks again. Awesome.
make my blog jump out. Please let me know where you got your design.
You have made some decent points there. I looked on the internet for more information about the issue and found most individuals will go along with your views on this site.
This is really interesting, You are a very skilled blogger. I have joined your feed and look forward to seeking more of your great post. Also, I ave shared your site in my social networks!
Thanks-a-mundo for the blog article.Really thank you! Will read on
The action comedy Red is directed by Robert Schewentke and stars Bruce Willis, Mary Louise Parker, John Malkovich, Morgan Freeman, Helen Mirren, Karl Urban and Brian Cox.
This website was how do I say it? Relevant!! Finally I have found something that helped me. Thank you!
I truly appreciate this blog article. Cool.
I really liked your article. Much obliged.
Wow, amazing blog structure! How lengthy have you ever been blogging for? you make blogging look easy. The whole look of your web site is excellent, as well as the content!
Very good blog.Really looking forward to read more. Cool.
It is truly a great and useful piece of info. I am glad that you shared this helpful information with us. Please keep us informed like this. Thank you for sharing.
Still, the site is moving off blogger and will join the nfl nike jerseys.
Paragraph writing is also a fun, if you be familiar with then you can write
I use pocket money also. I love it. I also use MPG and it allows me to record my gas purchases and maintenance transactions into pocket money right from MPG.
This is very interesting, You are a very skilled blogger. I ave joined your rss feed and look forward to seeking more of your excellent post. Also, I have shared your website in my social networks!
Way cool! Some extremely valid points! I appreciate you penning this post plus the rest of the site is also really good.
Well I definitely liked studying it. This post procured by you is very practical for good planning.
Wow! This could be one particular of the most useful blogs We have ever arrive across on this subject. Actually Wonderful. I am also an expert in this topic therefore I can understand your hard work.
pretty useful material, overall I imagine this is well worth a bookmark, thanks
still care for to keep it smart. I can at wait to read far more from you. This is actually a great site.
loves can you say that about?) louis vuitton hlouis vuitton handbags replicabags replica it as back this fall in mouth watering chocolate. How can you go wrong
Regards for helping out, fantastic information.
It as really a nice and helpful piece of info. I am glad that you just shared this helpful info with us. Please keep us informed like this. Thanks for sharing.
Very informative article post.Really looking forward to read more. Great.
It as hard to come by knowledgeable people on this subject, however, you sound like you know what you are talking about! Thanks
I truly appreciate this post. I have been looking all over for this! Thank God I found it on Google. You ave made my day! Thanks again.
Muchos Gracias for your article.Really looking forward to read more. Will read on
This blog is obviously entertaining and factual. I have found a lot of useful tips out of this amazing blog. I ad love to return over and over again. Thanks a lot!
This blog has lots of very useful stuff on it. Thanks for sharing it with me!
to continue your great job, have a nice afternoon!
your presentation however I find this topic to be really one thing
This is one awesome article post.Really looking forward to read more. Much obliged.
Say, you got a nice post.Much thanks again. Keep writing.
You must take part in a contest for top-of-the-line blogs on the web. I will suggest this web site!
w8ZGeM whoah this blog is magnificent i love reading your articles. Keep up the good work! You know, many people are hunting around for this info, you can aid them greatly.
It as challenging to find educated persons by this topic, nonetheless you sound in the vein of you already make out what you are speaking about! Thanks
Thanks-a-mundo for the post.Much thanks again. Awesome.
Very neat blog.Really thank you! Really Cool.
we like to honor many other world wide web websites around the internet, even if they aren
I think this is a real great article post. Much obliged.
Really appreciate you sharing this article.Really thank you! Want more.
You are my inspiration, I own few web logs and occasionally run out from brand . Truth springs from argument amongst friends. by David Hume.
What as Happening i am new to this, I stumbled upon this I have found It absolutely useful and it has helped me out loads. I hope to contribute & help other users like its helped me. Good job.
Wow, marvelous blog layout! How long have you been blogging for? you make blogging look easy. The overall look of your website is fantastic, as well as the content!