I am trying to add an initial unbound entry into a bound ComboBox. I am trying an approach very similar to the answer to the following post:
How to insert 'Empty' field in ComboBox bound to DataTable
However, the main difference is that I have strict requirements and cannot add a row to the actual datatable (as this is used by other components). So, my solution is to add the additional row to the dataview instead of the datatable:
public void FillVendorComboBox(DataSet1.VendorDataTable vendors)
{
//Create a custom view of the vendor table
DataView view = new DataView(vendors);
//Add a new row to the view with default values
DataSet1.VendorRow vendorRow = (DataSet1.VendorRow)view.AddNew().Row;
vendorRow.Name = "a";
//Sort the view according to the vendor name
view.Sort = vendors.NameColumn.ColumnName;
//Bind the view to the combo box
cbxVendor.DataSource = view;
cbxVendor.DisplayMember = vendors.NameColumn.ColumnName;
cbxVendor.ValueMember = vendors.IdColumn.ColumnName;
}
The problem is that the sorting is not working as expected. The added value is always sorted to the end of the ComboBox:
BC (bound)
Shell (bound)
a (added) <-- why is this sorting to the bottom?
Also please note that the VendorRow.Name is datatype System.String and VendorRow.Id is datatype System.Int32.
Related
My end goal is to populate a DataGrid with data retrieved from a database at runtime. My methodology is to pull all the columns from that database first and then pull the corresponding data for each column for each row as below:
ReadOnlyCollection<Field> tablefields = {//has the fields populated from the database};
//adding all the columns to the data table
var datatable = new DataTable("mytable");
foreach (var field in tablefields)
{ datatable.Columns.Add(field.Name, wFieldType); }
//Then add the rows
while (cursor.MoveNext())
{
var tableRow = datatable.NewRow();
var row = cursor.Current;
for(var i = 0; i < tablefields.Count; i++)//add the values for each field to the data table.
{
tableRow[i] = row[i] ?? DBNull.Value;
}
datatable.Rows.Add(tableRow);
}
My problem however is that some of the columns that I am pulling have to be in the form of a DataGridComboBoxColumn since they have preset values in a dropdown list in the database. The best case scenario would have been that as I pull the columns into my dataTable one by one, I would detect the ones that have preset values and make those oftype DataGridComboBoxColumn before adding them to the dataTable. I obviously can't do that because you cannot have a combobox on a dataTable. My next option is to use the dataGrid to load the columns and the data from the database at runtime.
I am able to add the columns with a combobox as shown in the code below but I cannot figure out how to get my data from the database into the dataGrid containing the pre-loaded columns.
DataGridComboBoxColumn cb = new DataGridComboBoxColumn();
cb.ItemsSource = new List(){to be populated later}
cb.Header = field.Name;
MyDataGrid.Columns.Add(cb);
The code above was used as a sample to test the DataGridComboBoxColumn and sure enough I was able to see the datagrid with the comboboxes. I just can't figure out how to get my database data into the table. I would appreciate any ideas.
PS: I cannot use datagridview because of the api that I am working with so I am limited to datagrid.
You should set the SelectedItemBinding to a binding to a column in your DataTable that contains the value in the ItemsSource to be selected:
cb.SelectedItemBinding = new Binding("AColumnInYourDataTable");
If you set the ItemsSource to a List<T> where T is a complex type, you should instead set the SelectedValueBinding and SelectedValuePath and DisplayMemberPath properties:
cb.SelectedValueBinding = new Binding("AColumnInYourDataTable");
cb.SelectedValuePath = "NameOfThePropertyOfTThatHoldsTheValue";
cb.DisplayMemberPath = "NameOfAPropertyOfTThatHoldsTheDisplayName";
I'm trying to create a DataGridView bound to a DataTable where one column is a ComboBox. The code runs but I get the following error after binding (not when data is bound): System.ArgumentException: DataGridViewComboBoxCell value is not valid.
In the DataGridView one of the columns is a DataGridViewComboBoxColumn that uses an enum (named structureType) as it's source:
// ColumnStructure
//
this.ColumnStructure.ValueType = typeof(structureType);
this.ColumnStructure.DataSource = Enum.GetValues(typeof(structureType));
this.ColumnStructure.HeaderText = "Structure";
this.ColumnStructure.Name = "ColumnStructure";
this.ColumnStructure.DataPropertyName = "Structure";
//
When I populate the DataGridView without using a DataTable, it works just fine:
structureType? structure = GetStructure(part);
dgvObjectTypes.Rows.Add(name, type, structure, count);
Now I would want to use a DataTable instead, but can't get it to work. The DataTable is created as follows:
DataTable table = new DataTable();
table.Columns.Add("Name", typeof(string));
table.Columns.Add("Type", typeof(string));
table.Columns.Add("Structure", typeof(DataGridViewComboBoxCell));
table.Columns.Add("Count", typeof(int));
Other columns work great but I can't get the "Structure" column to work. Here's how I've tried to create the combobox:
var cb = new DataGridViewComboBoxCell();
cb.ValueType = typeof(structureType);
cb.DataSource = Enum.GetValues(typeof(structureType));
cb.Value = (structureType)structure;
After that I just create the rows for the table and and set the table as datasource for the DataGridView:
table.Rows.Add(name, type, cb, count);
dgv.DataSource = table;
I've read a lot of posts where it has been stated that using enums in comboboxes causes problems (this for example: DataGridView linked to DataTable with Combobox column based on enum), but that doesn't seem to be the case here. I even tried to use explicitly typed arrays of strings but still get the same error. I think I'm doing something wrong with the DataGridViewComboBoxCell.
What could be the problem?
It seems like the step you are missing is providing the Names and Values for the CBO. The DataTable can store the value, and the DGV can display the related name, but you need to help provide the translation.
private enum structureType
{ None, Circle, Square, Pyramid}
...
dtStruct = new DataTable();
dtStruct.Columns.Add("Name", typeof(string));
dtStruct.Columns.Add("Type", typeof(string));
dtStruct.Columns.Add("Structure", typeof(structureType));
dtStruct.Columns.Add("Count", typeof(int));
// autogen columns == true
dgv2.DataSource = dtStruct;
// create DataSource as list of Name-Value pairs from enum
var cboSrc = Enum.GetNames(typeof(structureType)).
Select( x => new {Name = x,
Value = (int)Enum.Parse(typeof(structureType),x)
}
).ToList();
// replace auto Text col with CBO col
DataGridViewComboBoxColumn cb = new DataGridViewComboBoxColumn();
cb.ValueType = typeof(structureType);
cb.DataSource = cboSrc;
cb.DisplayMember = "Name"; // important
cb.ValueMember = "Value"; // important
cb.HeaderText = "Structure";
cb.DataPropertyName = "Structure"; // where to store the value
dgv2.Columns.Remove(dgv2.Columns[2]); // remove txt col
dgv2.Columns.Add(cb);
cb.DisplayIndex = 2;
// add data
dtStruct.Rows.Add("Ziggy", "Foo", structureType.Circle, 6);
The first part creates the DataTable, note that the Structure column type is structureType (or often int). The DataTable will be storing data, not DataGridViewComboBoxCell elements. In cases where the data comes from a database, that column would be int since structureType would not be a known type.
A DataSource is then created from the enum names and values. This provides the means for the control to show the name, yet store the value in the DataTable.
If the DGV is set to auto generate columns, you will need to replace the default TextBoxColumn with a ComboBoxColumn. This is being done after the DataSource has been set, but before any data is added. When the data comes from a DB (and so there is not usually an empty, typed table) you can use the ColumnAdded event to swap out one column for another.
The important thing when adding the CBO column is to set the ValueMember and DsiplayMember properties to provide the Value <-> Name translation and DataPropertyName so it knows where to store the selected value in the DataTable.
I have a dataview. Source of dataview is a view from sql server.
Dataview is sorted. Like this:
dv_building.Sort = "bld_number"
this dataview is a datasource to my datagird1:
DataGrid1.DataSource = dv_building
Now I want to remove sorting dv_building.Sort = String.Empty but the view of my data grid was changed, because sorting was removed, ie dataview was returned to their default sorting, probably by clustered index.
How can I remove sorting, but the actual sorting remain by bld_number? Is it possible?
I found this workaround to achieve requested behaviour: implementation of class derived from Datatable is accessible for me, additional column appended to Columns:
Columns.AddRange(
new DataColumn[N]
{
...
new DataColumn("Order", typeof (int))
}
I enumerate this column with order numbers after each data view column modification (column participated in sort). When I need freeze sorting I switch DataView.Sort property to this "Order" column. To unfreeze - dataView.Sort = regularSortCriterion.
I have a ComboBox control.
I bind to this control to the DataSet table.
Here is the code:
comboBox.Items.Add(("Select"));
comboBox.DataSource = DataSet.ColorTable;
comboBox.DisplayMember = DataSet.ColorTable.ColorNameColumn.ColumnName;
comboBox.ValueMember = DataSet.ColorTable.ColorIDColumn.ColumnName;
This result I get:
I want to display on the top of the list SELECT: word. So I need to add addition Item to the comboBox control.
Here how I implement it:
cmbCategory.Items.Add(("Select"));
But the result is still the same as above. I get only colors without SELECT: word on the top of the list.
Any idea how I can add this string-SELECT: to the ComboBox control and set to this string ValueMember?
Use Insert method instead.
cmbCategory.Items.Insert(0, "Select");
Note : Put this code after the databind.
You can add the collections of color to an array or a dataset (if you are getting them from database) first and then add items "select", then add each elements of the array or a column of the dataset.
Do this in Form_Load function and wherever there are changes made in color collections array or database.
//This will set Display member and value member
comboBox.DisplayMember = "ColorName";
comboBox.ValueMember = "ColorCode";
//This will add a new row to table in binded dataset
DataRow dr = dal.MyProperty_dsColors.Tables["ColorInfo"].NewRow();
dr["ColorName"] = "Select Color"; //SomeName
dr["ColorCode"] = 001; //Some ID
dal.MyProperty_dsColors.Tables["ColorInfo].Rows.Add(dr);
//binding dataSource
comboBox.DataSource = dal.MyProperty_dsColors.Tables["ColorInfo"];
What would also help you is that you set the ComboBox without having to 'Select' it when the popup arrives...
Select your ComboBox, under the properties tab, select Appearance->Drop Down Style and select DropDownList.
If we want to add manually values in a combobox (for example integers) this can be done using a for loop:
// sample code
int lower=1;
int higher=500;
for (int i=lower; i<=higher; i++)
combo_values.Items.Add(i.ToString());
Note that you have to use the int.Parse(combo_values.Text) command to read a value.
I'm creating a simple application that simply populates a DataGridView with a strongly typed DataTable. My problem is that all the cells show up empty despite containing data, I have a CellDoubleClick event which can pick up the underlying value of the cell (which is typed as expected) but the column is shown as empty.
Objects of type INode all have a ToString() method defined and my assumption was that the DataGridView would be intelligent enough to call that method any show that as the cell contents but it appears not.
Here is my current test code:
//Add some fake test data
DataTable data = new DataTable();
data.Columns.Add("Subject");
data.Columns.Add("Predicate");
data.Columns.Add("Object");
data.Columns["Subject"].DataType = typeof(INode);
data.Columns["Predicate"].DataType = typeof(INode);
data.Columns["Object"].DataType = typeof(INode);
Graph g = new Graph();
DataRow row = data.NewRow();
row["Subject"] = g.CreateURINode(new Uri("http://example.org/subject"));
row["Predicate"] = g.CreateURINode(new Uri("http://example.org/predicate"));
row["Object"] = g.CreateURINode(new Uri("http://example.org/object"));
data.Rows.Add(row);
this.dvwBrowser.DataSource = data;
this.dvwBrowser.AutoResizeColumns();
How can I get the value of the ToString() method to be displayed in the cells when I bind the DataTable to the DataGridView?
dvwBrowser columns are using a user-defined type (INode) not one of the base .NET Framework data types and Byte[]. INode have to be marked by the SerializableAttribute at least. You'll find further explanations here (at the bottom of the page, before the code example.)