DatagirdComboBoxColumn+binding source problem

Friends,

I have my Datagridview bind to single table binding source. Which has two columns "Option domain" and "Default Option Value", This columns are Dropdown Combos and the datasource is from Lookup dataset having two tables "Option domains" and "Option Domains Value" (Parent-child).

Based on the value of "Option domain" 1st column, "Default Option Value" combo will have filter list by that value. I tried the following:

(A) I tried to bind "Default Option value" to Parent-child relation of lookup dataset (OptionDomainOptionDefaultValueBindingSource).

PROBLEM: Here when displaying grid, valus for "Default option value" column are missing(not displayed) I guess because of its bindingsource is from relation it just have rows for the first value of "Option Domain" and thus rest of the rows are blank.

(B) SO, I changed my bindingsource to "Default Option Value" of lookup dataset. And in CellEnter event (for that column) I did, BindingSource.Filter for child rows. and in CellLeave in did BindingSource.RemoveFilter

PROBLEM: It now display all data in grid, Also when clicking "Default Option Value" combo it display me filtered rows, but after selecting value and leaving "Default Option Column" it gives me exception of "IndexOutOfRangeException" "Their is no row at position 3"

AM I MISSING SOMETHING HERE? ANY HELP on how to do this?

Thanks.
Dharmesh

[1390 byte] By [DharmeshVora] at [2007-12-16]
# 1

Can you post the code that is inside your CellEnter and CellLeave? I wanted to try and debug this.

-mark
DataGridView Program Manager
Microsoft
This post is provided "as-is"

MarkRideout at 2007-9-9 > top of Msdn Tech,Windows Forms,Windows Forms Data Controls and Databinding...
# 2

-code for the CellEnter/CellLeave

private void sAVFormElementRowDataGridView_CellEnter(object sender, DataGridViewCellEventArgs e)
{
try
{
if (e.ColumnIndex == this.dataGridViewTextBoxColumn30.Index)
{
this.OptionDomainsValuesBindingSource.Filter = "answer_group_id = " +
this.sAVFormElementRowDataGridView[e.ColumnIndex - 1, e.RowIndex].Value.ToString();
}
}
catch { }
}

private void sAVFormElementRowDataGridView_CellLeave(object sender, DataGridViewCellEventArgs e)
{
try
{
if (e.ColumnIndex == this.dataGridViewTextBoxColumn30.Index)
{
this.OptionDomainsValuesBindingSource.RemoveFilter();
}
}
catch { }
}

DharmeshVora at 2007-9-9 > top of Msdn Tech,Windows Forms,Windows Forms Data Controls and Databinding...
# 3

This is very close to what you need, but there are two things you need to do.

1) You need to have both a filtered view and an unfiltered view of your data. Here is how I did this using a DataTable:

// the ComboBox column is bound to the unfiltered DataView
unfilteredSubCatBS = new BindingSource();
DataView undv = new DataView(subCategoryDT);
unfilteredSubCatBS.DataSource = undv;
subCategoryComboBoxColumn.DataSource = unfilteredSubCatBS;
subCategoryComboBoxColumn.DisplayMember = "Name";
subCategoryComboBoxColumn.ValueMember = "ID";

// this binding source is where I perform my filtered view
filteredSubCatBS = new BindingSource();
DataView dv = new DataView(subCategoryDT);
filteredSubCatBS.DataSource = dv;

2) You need to perform your filtering in the CellBeginEdit and remove your filtering in the CellEndEdit. In addition, you need to switch in/out the DataSource for your combobox cell in the begin/end edit like so:

private void dataGridView1_CellBeginEdit(...)
{
try
{
if (e.ColumnIndex == subCategoryComboBoxColumn.Index)
{
DataGridViewComboBoxCell dgcb = (DataGridViewComboBoxCell)dataGridView1[e.ColumnIndex, e.RowIndex];
dgcb.DataSource = filteredSubCatBS;

this.filteredSubCatBS.Filter = "subid = " +
this.dataGridView1[e.ColumnIndex - 1, e.RowIndex].Value.ToString();
}
}
catch { }
}

private void dataGridView1_CellEndEdit(...)
{
try
{
if (e.ColumnIndex == this.subCategoryComboBoxColumn.Index)
{
DataGridViewComboBoxCell dgcb = (DataGridViewComboBoxCell)dataGridView1[e.ColumnIndex, e.RowIndex];
dgcb.DataSource = unfilteredSubCatBS;

this.filteredSubCatBS.RemoveFilter();
}
}
catch { }
}

Hope this helps,
-mark
DataGridView Program Manager
Microsoft
This post is provided "as-is"

MarkRideout at 2007-9-9 > top of Msdn Tech,Windows Forms,Windows Forms Data Controls and Databinding...
# 4

Hi Mark,

I did exactly same as you have mentioned. But now it does not accepts selected (Changed) value from dropdown to the cell. Any Idea?

-Dharmesh

DharmeshVora at 2007-9-9 > top of Msdn Tech,Windows Forms,Windows Forms Data Controls and Databinding...
# 5
I don't follow the "accepts selected (Changed) value" -- can you clarify?

-mark
Program Manager
Microsoft
This post is provided "as-is"

MarkRideout at 2007-9-9 > top of Msdn Tech,Windows Forms,Windows Forms Data Controls and Databinding...
# 6

I modified my code as you suggested and ran the application. I get grid displaying all data properly and it also display filtered rows in the dropdown. I went ahead and selected another item from the dropdown list.

And when I leave that cell by clicking other cell or tab key, that cell does not get updated with the value I selected from the dropdown list and displayed the original value it had. Sad


DharmeshVora at 2007-9-9 > top of Msdn Tech,Windows Forms,Windows Forms Data Controls and Databinding...
# 7

Ok. The code I provided worked on latest RTM bits, but I didn't try it on Beta2. Let me get a Beta2 box and see what works for you. Also - can you try on the latest July CTP bits (MSDN Subscriber Downloads)?

-mark
DataGridView Program Manager
Microsoft
This post is provided "as-is"

MarkRideout at 2007-9-9 > top of Msdn Tech,Windows Forms,Windows Forms Data Controls and Databinding...
# 8

I just tried this on Beta2 build (50215.00)and I didn't have any problems selecting a value from the combobox. I put a DGV on the form and I have four columns - a textbox, 2 combobox columns then another textbox column.

Here is my code:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace WindowsApplication3
{
public partial class Form1 : Form
{
DataTable categoryDT, subCategoryDT;
BindingSource catBS, filteredSubCatBS, unfilteredSubCatBS;
public Form1()
{
categoryDT = new DataTable("category");
categoryDT.Columns.Add("ID", typeof(int));
categoryDT.Columns.Add("Name", typeof(string));

subCategoryDT = new DataTable("subcategory");
subCategoryDT.Columns.Add("ID", typeof(int));
subCategoryDT.Columns.Add("subID", typeof(int));
subCategoryDT.Columns.Add("Name", typeof(string));

categoryDT.Rows.Add(new object[] { 0, "cat0" });
categoryDT.Rows.Add(new object[] { 1, "cat1" });
categoryDT.Rows.Add(new object[] { 2, "cat2" });

subCategoryDT.Rows.Add(new object[] { 0, 0, "SubCat0-Cat0" });
subCategoryDT.Rows.Add(new object[] { 1, 0, "SubCat1-Cat0" });
subCategoryDT.Rows.Add(new object[] { 2, 0, "SubCat2-Cat0" });
subCategoryDT.Rows.Add(new object[] { 3, 1, "SubCat3-Cat1" });
subCategoryDT.Rows.Add(new object[] { 4, 1, "SubCat4-Cat1" });
subCategoryDT.Rows.Add(new object[] { 5, 1, "SubCat5-Cat1" });
subCategoryDT.Rows.Add(new object[] { 6, 2, "SubCat6-Cat2" });
subCategoryDT.Rows.Add(new object[] { 7, 2, "SubCat7-Cat2" });
subCategoryDT.Rows.Add(new object[] { 8, 2, "SubCat8-Cat2" });

InitializeComponent();

catBS = new BindingSource();
catBS.DataSource = categoryDT;
categoryComboBoxColumn.DataSource = catBS;
categoryComboBoxColumn.DisplayMember = "Name";
categoryComboBoxColumn.ValueMember = "ID";

// the ComboBox column is bound to the unfiltered DataView
unfilteredSubCatBS = new BindingSource();
DataView undv = new DataView(subCategoryDT);
unfilteredSubCatBS.DataSource = undv;
subCategoryComboBoxColumn.DataSource = unfilteredSubCatBS;
subCategoryComboBoxColumn.DisplayMember = "Name";
subCategoryComboBoxColumn.ValueMember = "ID";

// this binding source is where I perform my filtered view
filteredSubCatBS = new BindingSource();
DataView dv = new DataView(subCategoryDT);
filteredSubCatBS.DataSource = dv;

}

private void dataGridView1_CellBeginEdit(object sender, DataGridViewCellCancelEventArgs e)
{
try
{
if (e.ColumnIndex == subCategoryComboBoxColumn.Index)
{
DataGridViewComboBoxCell dgcb = (DataGridViewComboBoxCell)dataGridView1[e.ColumnIndex, e.RowIndex];
dgcb.DataSource = filteredSubCatBS;

this.filteredSubCatBS.Filter = "subid = " +
this.dataGridView1[e.ColumnIndex - 1, e.RowIndex].Value.ToString();
}
}
catch { }

}

private void dataGridView1_CellEndEdit(object sender, DataGridViewCellEventArgs e)
{
try
{
if (e.ColumnIndex == this.subCategoryComboBoxColumn.Index)
{
DataGridViewComboBoxCell dgcb = (DataGridViewComboBoxCell)dataGridView1[e.ColumnIndex, e.RowIndex];
dgcb.DataSource = unfilteredSubCatBS;

this.filteredSubCatBS.RemoveFilter();
}
}
catch { }


}
}
}

-mark
Program Manager
Microsoft
This post is provided "as-is"

MarkRideout at 2007-9-9 > top of Msdn Tech,Windows Forms,Windows Forms Data Controls and Databinding...
# 9
Hi Mark,

Having closer look while trying your code, I found that I coded CellEnter/CellLeave events. I changed to CellBeginEdit/CellEndEdit and it works fine now.

My apology on not reading it carefully at first time and taking more of your time.

Thanks for your all your time and efforts to help me out Smile

Dharmesh

DharmeshVora at 2007-9-9 > top of Msdn Tech,Windows Forms,Windows Forms Data Controls and Databinding...
# 10

Hi Mark,

I just tried the code you provided on the issue: 2 comboboxes (in a master/detail relation) in different datagridviewcomboboxcolums. One combo holds information on warehouses (master), while the other holds the different locations (detail). Below you can find a snapshot of the code I'm using:

' Format the grdInvTrans DataGridView.

With grdInvTrans

.BackColor = Color.GhostWhite

.BackgroundColor = Color.Lavender

.BorderStyle = BorderStyle.None

WhseBS = New BindingSource()

WhseBS.DataSource = ds.Tables("Warehouse")

WhseCol.HeaderText = "Warehouse"

WhseCol.ValueMember = "WhseID"

WhseCol.DisplayMember = "WhseName"

WhseCol.DataSource = WhseBS

WhseCol.DataPropertyName = "WhseID"

.Columns.Add(WhseCol)

Dim undv As New DataView(ds.Tables("Location"))

UnFilteredLocBS.DataSource = undv

LocCol.DataSource = UnFilteredLocBS

LocCol.HeaderText = "Location"

LocCol.ValueMember = "LocID"

LocCol.DisplayMember = "LocationCode"

LocCol.DataPropertyName = "LocID"

.Columns.Add(LocCol)

Dim dv As New DataView(ds.Tables("Location"))

FilteredLocBS.DataSource = dv

End With

Private Sub grdInvTrans_CellBeginEdit(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewCellCancelEventArgs) Handles grdInvTrans.CellBeginEdit

' Limit the Location ComboBox with only values for the selected Warehouse.

If e.ColumnIndex = 18 Then

Dim dgcb As DataGridViewComboBoxCell = CType(grdInvTrans(e.ColumnIndex, e.RowIndex), DataGridViewComboBoxCell)

FilteredLocBS.Filter = "WhseID = " & CInt(grdInvTrans(e.ColumnIndex - 1, e.RowIndex).Value)

dgcb.DataSource = FilteredLocBS

End If

End Sub

Private Sub grdInvTrans_CellEndEdit(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewCellEventArgs) Handles grdInvTrans.CellEndEdit

Select Case e.ColumnIndex

Case 17 ' Warehouse ComboBox.

Case 18 ' Location ComboBox.

Dim dgcb As DataGridViewComboBoxCell = CType(grdInvTrans(e.ColumnIndex, e.RowIndex), DataGridViewComboBoxCell)

FilteredLocBS.RemoveFilter()

dgcb.DataSource = UnFilteredLocBS

End Select

End Sub

After selecting an item from the Warehouse combo and opening the Location Combo, I get an error saying the "Datagridviewcomboboxcell value is not valid". I think this has something to do with the fact that the selected Location value (in the combo) doesn't belong to the new Location combo filtered datasource.

Fyi: e.columnindex = 18 refers to the Location combo, and 17 to the Warehouse combo.

Please your advise on above issue.

Best regards,

Jouri

Jouri at 2007-9-9 > top of Msdn Tech,Windows Forms,Windows Forms Data Controls and Databinding...
# 11

Hello Mark,

I have reproduced this example with good success and it helped me get pretty far but I guess what I am doing is different enough that I am stuck. I hope you or another reader can help.

In my case... I only have one table:
Inventory
ID - Int64 - hidden
Name - String
Comment - String
ParentID - Int64 - the ComboBoxColumn
Quantity - Int64

My form has two DataSets, InventoryBindingSources, and InventoryTableAdapters, one set is prefixed with the word "filtered." The datagridview is bound to the InventoryBindingSource, and the ParentIDColumn is bound to the filteredInventoryBindingSource.

As the form loads, I add an "empty" row to filteredInventory with an ID of 0. Then I fill the datatable as normal. The empty row joins with the inventory items with no parent - and display no parent as a blank entry at the top of the downdown.

In the BeginCellEdit event I set the filteredBindingSource so that it only shows possible parents. It excludes the current record and any children to prevent circular references. In the EndCellEdit event I RemoveFilter.

Works groovy. All the parents show nicely and when I go to change one the list is properly filtered. But...

Now when I want to change a record to remove a parent - I select the blank line in the dropdown - it should have an ID of 0, thus the grid should have a parent id of 0. When I leave the cell I get this error:

Cannot set Column 'Parent ID' to be null, please use DBNull instead.

Thanks in advance for your advice - I've tried "everything" I can think of.

Glenn

GHolden at 2007-9-9 > top of Msdn Tech,Windows Forms,Windows Forms Data Controls and Databinding...