Using custom CheckBoxComboBox in DataGridView column - c#

I need to use the combobox control with items that can be checked in the DataGridView column. So i have found one control from the following link
https://github.com/sgissinger/CheckBoxComboBox/
In this the basic CheckBoxComboBox is working fine in the demo and it has extended DataGridViewCheckBoxComboBoxColumn class that can be used in DataGridView for a single column.
Now on adding this column in DataGridView on design time, with DataGrid enabled to add new rows, when a data is inserted in another column and value is selected from this column, the ParseFormattedValue() is giving null exception.
Also when adding a new row using code, then GetFormattedValue() is giving null exception in value parameter of it.
In DataGridView I have added two columns, one is simple textbox column and another is DataGridViewCheckBoxComboBoxColumn adding week day names in it.
Now when I try to add a new row by
dataGridView1.Rows.Add();
its showing exception in GetFormattedValue() as null is coming in value parameter of it.

The use of custom CheckBoxComboBox control in DataGridView is done. First I have added a DataGridViewTextBoxColumn or any other as per your requirement and then add the custom CheckBoxComboBox column in that DataGridView in following way.
First you need to create the list of items to be shown in that Combo Box
List<Status> statuses = new List<Status>();
statuses.Add(new Status(1, "Sunday"));
statuses.Add(new Status(2, "Monday"));
statuses.Add(new Status(3, "Tuesday"));
statuses.Add(new Status(4, "Wednesday"));
statuses.Add(new Status(5, "Thursday"));
statuses.Add(new Status(6, "Friday"));
statuses.Add(new Status(7, "Saturday"));
Then you need to create the object of DataGridViewCheckBoxComboBoxColumn
DataGridViewCheckBoxComboBoxColumn comboboxColumn = new DataGridViewCheckBoxComboBoxColumn();
Create an object of ListSelectionWrapper from that statuses List object and set its TextSeparator property.
ListSelectionWrapper<Object> wrappedList = new ListSelectionWrapper<Object>(statuses);
wrappedList.TextSeparator = comboboxColumn.TextSeparator;
Add comboboxColumn other properties as
comboboxColumn.DataSource = wrappedList;
comboboxColumn.ValueMember = "Selected";
comboboxColumn.DisplayMemberSingleItem = "Name";
comboboxColumn.DisplayMember = "NameConcatenated";
And then insert the column in DataGridView
dgvKioskList.Columns.Add(comboboxColumn);
After this your column is inserted in your DataGridView. Now to insert a new row with pre-selected some list items, you need to create a Dictionary object like
Dictionary<String, Object> objSelectedDays = new Dictionary<String, Object>();
If you want to select the Sunday and Tuesday from the ComboBox for example you could write
objSelectedDays.Add("Sunday", statuses[0]);
objSelectedDays.Add("Tuesday", statuses[2]);
After this when you insert a new row into DataGridView with your other data and this objSelectedDays object, the new row will be added to grid
datagridView1.Rows.Add("ID1", objSelectedDays);
where "ID1" is inserted into simple DataGridViewTextBoxColumn
If you want to read the selected values from the datagrid then type cast the cell into Dictionary object like
var values = datagridView1.Rows[0].Cells[1].Value as Dictionary<String, Object>;
and then loop though the values object array to read the selected values from the particular rows CheckBoxComboBox control

Related

Add dropdown list to DataGrid columns and populate rows at runtime C#

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";

DataGridView prevent auto increasing of id bound column

I have a database with id (identity) column. This column is bound to my DataGridView (WindowsForms). Whenever I add a row to DataGridView (it's not yet added to database, just to DataGridView), number in this column is increasing by one. Extremely annoying behaviour and I don't know how to stop it?
I am not using EF or antyhing similar. Just columns in DataGridView (not automatically generated, but user defined columns - AutoGenerateColumns is switched off), added to DataGridView and with each column set the DataPropertyName.
I don't know your code, but did you try to do this:
datagridview1.AllowUserToAddRows = false;
and make a button for adding the rows , and add it with
datagridview1.Rows.Add(-1,..,...);
Why not just hide the column?
dgvMyDataGridView.Columns["Id"].Visible = false;
I haven't found a way to prevent DataGridView from auto increasing id. But, I could change any value in DataGridView now, with the DataSource being bound to BindingList instead of DataTable, as before (each ComboBoxItem must implement INotifyPropertyChanged).
private void initializeDgvNotesDataSource()
{
List<ComboBoxItem> list = new List<ComboBoxItem>();
BindingList<ComboBoxItem> bindingList = new BindingList<ComboBoxItem>(list);
BindingSource bindingSource = new BindingSource();
bindingSource.DataSource = bindingList;
dgvNotes.DataSource = bindingSource;
}
Cast your integer type id column as string before binding to grid. like
select CAST(ID as varchar(100)) as from yourtablename.
You can also set the default value to identify the new record.

How to get value of ValueMember of datagridviewcomboboxcolumn

Friends, I'm using datagridviewcomboboxcolumn as column index 1 in my datagridview. I've fetched data from access database table and populated the datagridviewcomboboxcolumn by following manner:
for (int i = 0; reader.Read(); i++)
{
cmbBusCode.Items.Add(reader["BUSINESS_CODE"] + "-" + reader["BUSINESS_DESCRIPTION"]);
cmbBusCode.ValueMember = "BUSINESS_CODE";
}
Then I'm writing this:
cmbBusCode.DisplayIndex = 1;
cmbBusCode.Width = 200;
cmbBusCode.Name = "Code";
cmbBusCode.HeaderText = "Code";
And adding this column as
dgView.Columns.Add(cmbBusCode);
Combobox is being populated and I can select any one from the list. Now when I'm saving data I want to get the ValueMember of the selected item. To get this value I'm using following code but It's giving the string representation of the field "BUSINESS_CODE" not the value..Please help me so that get ValueMemeber of the selected item..
foreach (DataGridViewRow row in dgView.Rows)
{
string cd = row.Cells["Code"].Value.ToString();
}
The "item" you are adding to the column doesn't have a "BUSINESS_CODE" column (essentially, the item as you add it is simply a string), so that doesn't work.
What you need to do is assign items that contain multiple columns. One must be the BUSINESS_CODE column (as it is this column you want to be the value for the underlying field in the DataGridView), the other should be a display column that contains the concatenated value you're adding at the moment (DisplayMember and ValueMember).
Easiest would be to create a typed data set, add a table that contains the two columns I described and fill the table with the data from the data reader.
Then, add the table as the data source for the column.
You can try adding a DataGridViewComboBoxCell instead of a string to the cmbBusCode.Items collection. You can then specify Value in the cell. I'm not sure if this will work, but it's worth a try.

c# datagridview having different datasources for different columns

I have a datagridview which has multiple text columns and a single comboboxcolumn. I need to specify a datasource for the iems of the comboboxcolumn and another datasource for the other text columns. I have tried the following sample code but it does not work.
dgv1.DataSource = DataSet1.Tables[0];
string[] managerList = Array.ConvertAll(DataSet2.Select(), row => (string)row[0]);
comboboxcolumn1.DataSource = managerList;
The managerList array has the entries that are to be populated into the combobox but they never show up.
Is that so that I can not have a separate datasource for a comboboxcolumn and its parent datagridview?
Thanks in advance.
I have checked with the following code and it works fine
string[] arr = "This Should Get Displayed".Split(); //managerList in your case
dataGridView1.DataSource = MyData(false);
(dataGridView1.Columns["Column1"] as DataGridViewComboBoxColumn).DataSource = new BindingSource(arr, null); // Bind to the column of grid
Can you try assigning it as referencing it as a column of your datagridview.
This displays the text split on space in my combobox.
When you are using string array no need to specify Display and Value member for the column
EDIT
If you have a DataTable you can also do it in this fashion
(dataGridView1.Columns["Column1"] as DataGridViewComboBoxColumn).DataSource= datatable;
(dataGridView1.Columns["Column1"] as DataGridViewComboBoxColumn).DisplayMember="columnname";
(dataGridView1.Columns["Column1"] as DataGridViewComboBoxColumn).ValueMember="columnname";

Insert a default row into a combobox that is bound to a datatable?

On a winform there is a combobox that derives its information from a datatable. The datatable draws from a database list.
this.cboList.DataSource = pullData();
this.cboList.DisplayMember = "fieldA";
Once the DataSource is set I am not able to insert a default row (ie *) as the first item in the combobox.
I tried this:
this.cboList.Items.Insert(0,"*");
Is there a way to insert in the combobox after the datasource is set or should this be done in the datatable?
UPDATE1:
The solution looks something like this:
var list = mydt.AsEnumerable().Select(row => row.Field<string>(fieldName)).ToList();
list.Insert(0, "*");
Where mydt is a populated datatable and fieldName is a variable holding the database field name.
Don't modify your data at the source just to make your UI work. Instead, perhaps extract your column into a list that you can modify before attaching it to the combo box.
var list = table.AsEnumerable().Select(row => row.Field<string>("fieldA")).ToList();
list.Insert(0, "*");
this.cboList.DataSource = list;
If a "Select None", or "*" is a valid select option it needs to come from the binding source object. I have done this in the past by adding a default record to a collection before binding it to a combo box.

Categories