Check if DataGridViewComboBox is empty - c#

I have a datagridview with a combobox column that is bound to an enum as follows:
var D = (DataGridViewComboBoxColumn)dgvInputs.Columns[2];
D.ValueType = typeof(MyType);
D.ValueMember = "Value";
D.DisplayMember = "Display";
D.DataSource = new MyType[] {
MyType.Rev,
MyType.Model,
MyType.User,
MyType.Status
}.Select(x => new { Display = x.ToString(), Value = (int)x }).ToList();
The datagridview is then bound to a DataTable named ParameterTable:
BindingSource ParamSource = new BindingSource();
ParamSource.DataSource = DataEntry.ParameterTable;
dgvInputs.AutoGenerateColumns = false;
dgvInputs.DataSource = ParamSource;
dgvInputs.Columns[0].DataPropertyName = "Name";
dgvInputs.Columns[1].DataPropertyName = "Prompt";
dgvInputs.Columns[2].DataPropertyName = "Type";
dgvInputs.Columns[3].DataPropertyName = "Width";
dgvInputs.Columns[4].DataPropertyName = "Default Value";
When the user finishes editing the table, I need to validate it. In particular, I need to test that the Type has been defined in each row, and that the Default Value is compatible with the Type.
The problem is, every test I've found for checking if the Type has been set has failed. When I later try to cast the value as MyType as part of testing the default value, I get an error. When I check the .Value property on the empty Type cell in the debugger, it shows a value of "{}".
Currently, I have this code for the test, in the Validating event for the datagridview itself. I have tried various other versions and they have also failed:
foreach (DataGridViewRow Row in dgvInputs.Rows) {
if (!Row.IsNewRow) {
// test other columns ...
DataGridViewComboBoxCell Cell = (DataGridViewComboBoxCell)(Row.Cells[2]);
if (Cell == null || Cell.Value as string == string.Empty) {
// Error ...
}
MyType PType = (MyType)(Cell.Value);
How can I test if a DataGridViewComboBox cell has not been set, and what is this value "{}"?
FYI - I am using VS 2008, and .Net 3.5 SP1. Not my choice. Just what is available to me.

There are a couple problems with this code.
First, D.ValueType = typeof(MyType); is incorrect because from what I see, you are binding to int field. Just remove that line, ValueType will be inferred from the data source.
Now, the main issue. When binding to a data table, the non entered value is represented by DBNull.Value. I would suggest you checking for both null and DBNull.Value. When entered, the value type in your case will be int, but you can safely unbox it to MyType.
The code should be something like this
//...
var value = Row.Cells[2].Value;
if (value == null || value == DBNull.Value)
{
// Error ...
}
else
{
var type = (MyType)value;
// Check if type contains a valid value ...
}

Related

DataGridViewComboBoxCell Value not being set properly

I've been trying to set the value of an individual DataGridViewComboBoxCell for the last 4 hours and I've been getting nowhere. The most common solution I've seen was to set the .Value member of the DataGridViewComboBoxCell to one of the Items, which I tried and it complained the value was not valid.
DataTable documentTypes = _codedValues.GetCodedValues(Database.DOCUMENT_TYPE_TABLE); documentTypes.Columns[Database.PROFESSION_ID_COLUMN].AllowDBNull = true;
documentTypes.Columns[Database.CODE].AllowDBNull = true;
this.cbxDocumentType.DisplayMember = Database.VALUE;
this.cbxDocumentType.ValueMember = Database.CODE;
this.cbxDocumentType.DataSource = documentTypes.DefaultView;
int rowId = this.dgvDocumentList.Rows.Add(doc.actualName, doc.fileName);
DataGridViewComboBoxCell obj = (DataGridViewComboBoxCell)this.dgvDocumentList.Rows[rowId].Cells[2];
obj.Value = obj.Items[0];
After the message box comes up that tells me DataGridViewComboBoxCell view is not valid, I see the .ToString output of the object being set, which is System.Data.DataRowValue.
Depending what your datasource contains you must use the correct casts to access the correct fields.
Try this:
DataGridViewComboBoxCell cell =
(DataGridViewComboBoxCell)this.dgvDocumentList.Rows[rowId].Cells[2];
cell.Value = ((DataRowView)cell.Items[0]).Row.ItemArray[0];
This assumes that the Items are DataRowViews and that the ValueMember is in the first field.
You can test the type by writing an intermediate step:
var item = cell.Items[0];
And use the debugger to look into the resulting type..

combobox shows value that isn't in the datasource

I have a combobox (using databindings)
I load the combobox values as follows:
cb.BeginUpdate();
cb.DisplayMember = "Display";
cb.ValueMember = "Value";
cb.DataSource = cbvalues.GetBindingTable(true);
if (cb.Items != null && cb.Items.ValueList != null)
cb.Items.ValueList.FormatFilteredItems = DefaultableBoolean.True;
cb.EndUpdate();
the cbvalues list contains several members (for example true/false/both)
the databindings occur as follows:
cb.DataBindings.Add("Value",
_data,
_data.GetPropertyName(bo => bo.cbvalue),
false,
DataSourceUpdateMode.OnPropertyChanged).BindingComplete += someotherfunctionname;
Now for the sake of reason, I for example always get the value Unknown from my object.
I would like to have my combobox showing nothing at all, just an empty combobox. Instead I get the value 'Unknown' in it. How can I disable this kind of behaviour.

AllowUserToAddRows causing Null Reference Exception in DataGridViewCellFormattingEvent

So I have a method called on the DataGridViewCellFormattingEvent that inspects the first column of my datagridview. If a row is found to have a value in the first column set to either an H or an F, it colors all the cells in that row one way or another, but then also sets the row to ReadOnly, Selected = false, and font to Bold. This is my workaround to including additonal Headers and Footers...it works great, except for this one problem.
If I set the datagridview AllowUserToAddRow property to true (Which ultimately we need to do), this method throws an invalid reference exception when it tries to get the cell value. The value is returning null, and I tried various methods to pass out of the method if that value is null, but it just keeps throwing the error no matter what I do. I would appreciate it if someone could offer some advice. Here is my method (note the only way I can get it to work is by setting the AllowUserToAddRows to false...I didn't normally have it in this method but included it for reference):
private void dgv_CellFormatting(object sender, DataGridViewCellFormattingEventArgs args)
{
var dgv = (DataGridView)sender;
dgv.AllowUserToAddRows = false;
if (!dgv.Rows[args.RowIndex].Cells[0].Value.Equals(null))
{
string cellValue = dgv.Rows[args.RowIndex].Cells[0].Value.ToString();
bool regF = Regex.Match(cellValue, "F.*").Success;
bool regH = Regex.Match(cellValue, "H.*").Success;
if (regH == true)
args.CellStyle.BackColor = System.Drawing.Color.LightBlue;
if (regF == true)
args.CellStyle.BackColor = System.Drawing.Color.LightGray;
if (regH == true || regF == true)
{
args.CellStyle.Font = new System.Drawing.Font(args.CellStyle.Font, FontStyle.Bold);
dgv.Rows[args.RowIndex].Cells[args.ColumnIndex].Selected = false;
dgv.Rows[args.RowIndex].ReadOnly = true;
}
}
}
This is the line that breaks:
if (!dgv.Rows[args.RowIndex].Cells[0].Value.Equals(null))
I tried using IsNullOrEmpty, doing a direct ... == null, all with the same result
Sorry if I wasted anyone's time with this, but I found a resolution. Instead of directly comparing null to my referenced value, I set the value as a DataGridViewCell object first, then I was able to properly compare its value to null. Here is the update I made:
DataGridViewCell c = dgv.Rows[args.RowIndex].Cells[0];
if (c.Value != null)

XtraTreeList - How can I reset the value of Unbound Boolean Cell to Indeterminate?

I have an XtraTreeList with an UnboundColumn of type Boolean, i.e.:
column.UnboundType = DevExpress.XtraTreeList.Data.UnboundColumnType.Boolean;
Initially, all CheckEdits are displayed 'grayed', in the Indeterminate State, and their value is Null.
Is there any way I can reset the values of particular check boxes to this Indeterminate state?
I have tried:
treeListNode[columnID] = null;
but an Error Message Box pops up, with the message: "Null object cannot be converted to a value type."
Also:
treeListNode[columnID] = DefaultBoolean.Default;
and:
treeListNode[columnID] = CheckState.Indeterminate;
but both set the cell's value to True.
Any help would be much appreciated.
Not sure if this still helps you but the other approach that you can try is setting the unbound column type to object and later restoring it.
foreach (var column in treeListNode.Columns)
{
var tc= column as TreeListColumn;
if (tc!= null && tc.Name == columnID)
{
var originalType = tc.UnboundType;
tc.UnboundType = UnboundColumnType.Object;
treeListNode[columnID] = null;
tc.UnboundType = originalType;
break;
}
}
try:
treeListNode[columnID] = DBNull.Value
but I am not sure it works, eventually we have to set the state of the CheckEdit manually.

DatagGridViewColumn.DataPropertyName to an array element?

I'm using a DataGridView binding its datasource to a List, and specifying the properties for each column.
An example would be:
DataGridViewTextBoxColumn colConcept = new DataGridViewTextBoxColumn();
DataGridViewCell cell4 = new DataGridViewTextBoxCell();
colConcept.CellTemplate = cell4;
colConcept.Name = "concept";
colConcept.HeaderText = "Concept";
colConcept.DataPropertyName = "Concept";
colConcept.Width = 200;
this.dataGridViewBills.Columns.Add(colConcept);
{... assign other colums...}
And finally
this.dataGridViewBills.DataSource=billslist; //billslist is List<Bill>
Obviously Class Bill has a Property called Concept, as well as one Property for each column.
Well, now my problem, is that Bill should have and Array/List/whateverdynamicsizecontainer of strings called Years.
Let's assume that every Bill will have the same Years.Count, but this only known at runtime.Thus, I can't specify properties like Bill.FirstYear to obtain Bill.Years[0], Bill.SecondYear to obtain Bills.Years[1]... etc... and bind it to each column.
The idea, is that now I want to have a grid with dynamic number of colums (known at runtime), and each column filled with a string from the Bill.Years List. I can make a loop to add columns to the grid at runtime depending of Bill.Years.Count, but is possible to bind them to each of the strings that the Bill.Years List contains???
I'm not sure if I'm clear enough.
The result ideally would be something like this, for 2 bills on the list, and 3 years for each bill:
--------------------------------------GRID HEADER-------------------------------
NAME CONCEPT YEAR1 YEAR2 YEAR3
--------------------------------------GRID VALUES-------------------------------
Bill1 Bill1.Concept Bill1.Years[0] Bill1.Years[1] Bill1.Years[2]
Bill2 Bill2.Concept Bill2.Years[0] Bill2.Years[1] Bill2.Years[2]
I can always forget the datasource, and write each cell manually, as the MSFlexGrid used to like, but if possible, I would like to use the binding capabilities of the DataGridView.
Any ideas? Thanks a lot.
I recently ran into this same problem. I ended up using DataGridView's virtual mode instead of binding to a data source. It doesn't have exactly the same features as binding, but it's still a lot more powerful than populating each cell manually.
In virtual mode, the DataGridView will fire an event whenever it needs to display a cell, which essentially means you can populate the cell however you please:
private void my_init_function() {
datagridview.VirtualMode = true;
datagridview.CellValueNeeded += new System.Windows.Forms.DataGridViewCellValueEventHandler(datagridview_CellValueNeeded);
}
private void datagridview_CellValueNeeded(object sender, DataGridViewCellValueEventArgs e)
{
e.Value = get_my_data(e.RowIndex, e.ColumnIndex);
}
You could use reflection to set up and fill the DataGridView. I've done this with a single type, but I don't see why it couldn't be extended to your data structure.
To set up the DataGridView:
// Create the columns based on the data in the album info - get by reflection
var ai = new AlbumInfo();
Type t = ai.GetType();
dataTable.TableName = t.Name;
foreach (PropertyInfo p in t.GetProperties())
{
// IF TYPE IS AN ARRAY (OR LIST) THEN ADD A COLUMN FOR EACH ELEMENT
var columnSpec = new DataColumn();
// If nullable get the underlying type
Type propertyType = p.PropertyType;
if (IsNullableType(propertyType))
{
var nc = new NullableConverter(propertyType);
propertyType = nc.UnderlyingType;
}
columnSpec.DataType = propertyType;
columnSpec.ColumnName = p.Name;
dataTable.Columns.Add(columnSpec);
}
dataGridView.DataSource = dataTable;
Then to populate the DataGridView:
// Add album info to table - add by reflection
var ai = new AlbumInfo();
Type t = ai.GetType();
// WOULD NEED TO INCREASE BY LENGTH OF ARRAY
var row = new object[t.GetProperties().Length];
int index = 0;
foreach (PropertyInfo p in t.GetProperties())
{
// IF TYPE IS AN ARRAY (OR LIST) THEN ADD EACH ELEMENT
row[index++] = p.GetValue(info, null);
}
dataTable.Rows.Add(row);
This is just the code I used, so you'll have to modify the code to handle your year array/list.

Categories