I'm using a RadGridView to display items. On a doubleclick the user comes to a detail view where he can edit the contents and then comes back (both usercontrols are displayed as tabs on screen and are thus existing simultaneously).
I'm using a working method to update the edited list in the grid view without reloading the whole gridview:
dataRow = (GridViewRowInfo)element.Data;
Worker displayedWorker = (Worker)edataRow.DataBoundItem;
Worker changedWorkerFromDataBase = GetWorkerFromDataBase(displayedWorker.WorkerNumber).FirstOrDefault();
List<Worker> tableDataSource = (List<Worker>)MyGridView.DataSource;
int indexInTableDataSource = tableDataSource.IndexOf(tableDataSource.Where(e => e.WorkerNumber == displayedWorker.WorkerNumber).First());
tableDataSource[indexInTableDataSource] = changedWorkerFromDataBase;
dataRow.InvalidateRow();
Like mentioned this code works, but when I change the IndexOf method to:
int indexInTableDataSource = tableDataSource.IndexOf(displayedWorker);
it doesn't work any longer as expected. The above code works once and only once after a second save the objects stored inside the MyGridView.DataSource and the one stored inside the dataRow.DataBoundItem are seen as being different resulting in the indexOf returning -1.
This behaviour surprised me quite a lot especially as the first method works without problems and I had thought that the dataBound item of the row is just a pointer towards the items inside the GridView datasource.
Thus my question here is: Can (after the update is done) I tell the row to update/refresh its databound item so that it is equal again to the one from the gridview (aka seen as the same object) ?
First off: If you have sorting/grouping enabled, the index in the datasource might not correspond with the index in the radgridview (see Childrows vs Rows).
I have worked on a similar scenario with a radgridview and a detailview.
To notify the radgridview that a propertyvalue is changed your Worker class needs to implement the INotifyPropertyChanged interface. About INotifyPropertyChanged
Change your datasource type from List to BindingList as this list has events raised when the list is changed. About BindingList
Now you should be able to alter the instance of your Worker class and the changes should be picked up immediatly in your radgridview.
The problem in your case is coming from the fact that for the grid's DataSource you are using List, which does not support notifications. If you switch to BindingList you will be good to go. Here is a sample:
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
AddGrid();
BindingList<Worker> lst = new BindingList<Worker>() { new Worker() { ID = 1, Name = "Adam" }, new Worker() { ID = 2, Name = "Eva" } };
radGridView1.DataSource = lst;
}
class Worker
{
public int ID { get; set; }
public string Name { get; set; }
}
private void radButton1_Click(object sender, EventArgs e)
{
Worker displayedWorker = (Worker)radGridView1.CurrentRow.DataBoundItem;
BindingList<Worker> tableDataSource = (BindingList<Worker>)radGridView1.DataSource;
int indexInTableDataSource = tableDataSource.IndexOf(displayedWorker);
Worker changedWorkerFromDataBase = new Worker() { ID = 1, Name = "new name" };
tableDataSource[indexInTableDataSource] = changedWorkerFromDataBase;
}
More on the difference between List and BindingList can be found here: https://stackoverflow.com/a/2244039/298871
Related
I want to display a table. Data for table must to be precalculated and it is heavy calculation. Also result table is long.
I want to provide acces to part of data while another part is calculated in the background.
I wrote background worker for calculation and DataGridView for display. Sometimes worker submit new rows via ProgressChangedEvent. I am tried to add this rows at bottom.
My problem starts when I tried add new rows at bottom. My UI is freezes.
Here is how I initialize DataGridView:
private void InitDataGridView()
{
BindingList<TableRow> tableRows = new BindingList<TableRow>();
dataGridView.DataSource = tableRows;
}
and here is how I adding new rows at bottom in ProgressChangedEvent
private void UpdateDataGridView (List<Items> newItems)
{
BindingList<TableRow> dataSource = (BindingList<TableRow>)this.dataGridView.DataSource;
foreach (var item in newItems)
dataSource.Add(new TableRow(item));
}
I think that problem is because adding one row initiate redrawing of the table.
But I don't found AddRange for BindingList or mechanism to stop rendering temporary.
Any suggestions?
By adding a new item to the list, BindingList raises a ListChanged which leads to updating DataGridView. To make updating of DataGridView more efficient, as an option you can stop raising ListChanged event of the BindingList. This way you can turn it off before adding items and then turn it on and raise the event just after adding a group of items to the list.
Here is an implementation which supports turning ListChanged event on/off:
public class MyBindingList<T>:BindingList<T>
{
public bool EnableChangeNotifications { get; set; }
protected override void OnListChanged(ListChangedEventArgs e)
{
if(EnableChangeNotifications)
base.OnListChanged(e);
}
}
And for example, disable the event when adding items::
list.EnableChangeNotifications = false;
//Add new items to the list using a loop or something else.
Then to showing changes in DataGridView, enable the event and raise it:
list.EnableChangeNotifications = true;
list.ResetBindings();
Try setting the AutoSizeColumnsMode property to None or DisplayedCells, as well as set on the AutoSizeMode on each column:
foreach (DataGridViewColumn c in thisGrid.Columns)
{
c.AutoSizeMode = DataGridViewAutoSizeColumnMode.None;
}
Try toggling this before and after binding, and when updating.
Also, you can try what is suggested on this answer and enable double buffering:
if (!System.Windows.Forms.SystemInformation.TerminalServerSession)
{
Type dgvType = dataGridView1.GetType();
PropertyInfo pi = dgvType.GetProperty("DoubleBuffered",
BindingFlags.Instance | BindingFlags.NonPublic);
pi.SetValue(dataGridView1, value, null);
}
I'm writing a WPF application connected to a local Access database. In one of the application screens, one table data (named Service) is shown in individual textboxes, like a form, and the user can navigate through records, create new ones, delete, edit or search. Everything is done on the same table.
After a intensive research on how to navigate through records displayed in textboxes, I ended up using a DataSet and a CollectionView.
public partial class Entries : Window
{
AgendaDataSet agendaDataSet = new AgendaDataSet();
AgendaDataSetTableAdapters.ServiceTableAdapter serviceAdapter = new AgendaDataSetTableAdapters.ServiceTableAdapter();
CollectionView workSheetView;
private void Window_Loaded(object sender, RoutedEventArgs e)
{
this.serviceAdapter.FillByDateAsc(agendaDataSet.Service);
this.DataContext = agendaDataSet.Service;
this.workSheetView = (CollectionView)CollectionViewSource.GetDefaultView(agendaDataSet.Service);
this.workSheetView.MoveCurrentToLast();
}
I got record navigation working using the CollectionView methods MoveCurrentToFirst(), MoveCurrentToNext(), etc. I also can create new records, edit and delete.
This is the method I use to create a new record:
private void btnNovo_Click(object sender, RoutedEventArgs e)
{
dynamic row = this.agendaDataSet.Service.NewMainRow();
this.agendaDataSet.Service.AddMainRow(row);
this.workSheetView.MoveCurrentToLast();
}
My problem is with record searching. I have a button that, when the user presses it, it asks for the PatientName he is searching. Then, the data about that Patient must appear on the various textboxes, ready to be consulted, edited or deleted.
Through the CollectionView, I only found the method GetItemAt() that gets a record based on it's row index. Since I am working with an Access database, I can't use the predicate ROW_NUMBER. And I don't think this approach would be the best.
So, how can I get an item based on it's ID, or PatientName, or any other field, and pass it as a row to the CollectionView?
Probably you don't need to get an item based on its ID or PatientName property.
Suppose that the user looks for "Andrew" as PatientName. Your code finds that the second row of your DataTable (called "Service") is the one the user is looking for.
You can use a simple static method to look for a DataRowView, something like this:
private static DataRowView FindDataRowView(DataView dataView, DataRow dataRow)
{
foreach (DataRowView dataRowView in dataView)
{
if (dataRowView.Row == dataRow)
{
return dataRowView;
}
}
return null;
}
and then you can select the object in your CollectionView:
collectionView.MoveCurrentTo(FindDataRowView(agendaDataSet.Service.DefaultView,
agendaDataSet.Service.Rows[2]));
Of course you can find the real DataRow index by using a foreach cycle or the Select method of DataTable.
I have added a record to the underlying db and after I add the record i do a
datagridview.Refresh(); and i dont see the newly added record.
If i stop and start the application its there. What am I doing or not
doing?
Notes : button1 and datagridview is in different Forms.I made datagridview's Modifiers public. This project is ado.net project
public class CustomerService
{
public List<Customers> ShowAll()
{
List<Customers> customers = new List<Customers>();
SqlConnection conn = new SqlConnection("data source=.; database=custer; user id=sa; password=*****");
SqlCommand cmd = new SqlCommand(" select * from Customers ", conn.Open());
SqlDataReader dr = cmd.ExecuteReader();
while (dr.Read())
{
Customer customer = new Customer ()
{
CustomerID = dr.GetInt32(0),
CustomerName = dr.GetString(1),
CustomerSurname = dr.GetString(2),
};
customers.Add(customer);
}
conn.Close();
return customers;
}
}
private void button1_Click(object sender, EventArgs e)
{
CustomerService service = new CustomerService();
if (txtCustomerName.Text != "" || txtCustomerSurname.Text != "")
{
customerservice.customerAdd(txtCustomerName.Text, txtCustomerSurname.Text);//this rows is other method .I am using for adding new customer
MessageBox.Show("Customer Added");
Form1.dataGridView1.DataSource = service.ShowAll();
Form1.dataGridView1.Refresh();
}
else
{
//……………
}
}
After adding the data to the DB, your application knows nothing about the data added. You need to load those data to memory. Retrieve all the data you want to display from the database and bind it explicitly to UI after doing operations on database.
EDIT:
Can you check what ShowAll() returns while debugging? Does it return you the data you actually want?
If it is a WPF application take a look here. The issue here is to tell your UI component that underlying data source has been changed, so the UI component reflects the changes made to data.
Again if it is a WPF application, you can define your data as ObservableCollection and bind you UI to this collection. ObservableCollection automatically requests UI to refresh when data has changed.
Rica ederim .)
The usual way of doing this is to reset the DataSource of the DataGridView.
Try like this code (with correct code to provide the right datasource):
Form1.dataGridView1.DataSource = typeof(List);
Form1.dataGridView1.DataSource = service.ShowAll();
Calling .Refresh() doesn't work since it only forces a repaint, but the code that paints the grid doesn't know of the changes.
Ref this WPF link also, that may help you:
Why does the DataGrid not update when the ItemsSource is changed?
Try calling EndEdit on the datagridview:
this.dataGridView1.EndEdit();
Second, refresh the grid view:
this.dataGridView1.Refresh();
And if that still doesn't work, try calling Refresh on the containing control
ParentControl.Refresh()
This will end up calling a repaint that might be needed.
what is the content of customerservice.customerAdd ?
Maybe it doesnt close the connection properly/doesnt flush the data into the db,and it only happens when you close your app(all memory is disposed,and all connections are closed/flushed).
Also - I suggest using a BindingSource that the grid is bound to,and changing its data source - it has event to notify the grid automaticallly if its data source has changed and that will cause it to refresh.
As you said these are in different forms, I think the pointer to Form1 doesn't point to the form that you want. you should pass the pointer of that form with this to this form.
When you are creating form 2 define like this:
Form2 = new Form2();
Form2.form1 = this;
Then your code should work.
It appears you're using a List as the datasource. I've found using a generic list is ok for read only data but for doing any kind of updates you need something with more power. I don't know about WPF but in winForms I've had great success with the IBindingList interface and the BindingList generic collection. The BindingList generic collection implements the IBindingList interface for you. I would read the MSDN articles on both of them. I've pretty much stopped using the IBindingList interface but it's still perfectly fine to implement.
http://msdn.microsoft.com/en-us/library/system.componentmodel.ibindinglist.aspx
http://msdn.microsoft.com/en-us/library/ms132679.aspx
The answer is to have the gridview connected to the BindingList<Customers> rather than the List<Customers> . Hope this will solve your problem...
If you change the List to a BindingList you'll have success. I threw together a sample which just had a DataGridView and a couple of Buttons on a Form.
button1 generates some fake data and assigns the datasource.
button2 adds another Customer to the underlying list.
The DataGridView is updated when the underlying BindingList changes.
Code:
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public string SurName { get; set; }
}
public partial class Form1 : Form
{
BindingList<Customer> customers = new BindingList<Customer>();
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
for (int i = 0; i < 10; ++i)
{
customers.Add(new Customer
{
Id = i,
Name = "Name" + i,
SurName = "Surname" + i
});
}
dataGridView1.DataSource = customers;
}
private void button2_Click(object sender, EventArgs e)
{
customers.Add(new Customer
{
Id = 22,
Name = "Newname",
SurName = "Newsurname"
});
}
}
Now, the reason this works is that BindingList<T> implements IBindingList, among other things, and that interface has, among other things, an event called ListChanged which is raised when the list or something in the list changes.
If you are not bound to use customer list strictly as data-source for datagridview then
Here is much better solution using datatable. You will get updated customer list and datagridview after insert
public class CustomerService
{
public DataTable ShowCustomers()
{
string cns = "data source=.; database=custer; user id=sa; password=*****";
SqlConnection conn = new SqlConnection(cns);
SqlDataAdapter da = new SqDataAdapter("select * from Customers", conn);
DataTable dt = new DataTable();
da.Fill(dt);
return dt;
}
}
private void button1_Click(object sender, EventArgs e)
{
CustomerService service = new CustomerService();
if (txtCustomerName.Text != "" || txtCustomerSurname.Text != "")
{
customerservice.customerAdd(txtCustomerName.Text,txtCustomerSurname.Text);
MessageBox.Show("Customer Added");
DataTable dt = service.ShowCustomers();
Form1.dataGridView1.DataSource = dt;
//If you also need customer list. Provide the DataTable and get list
List<Customers> customers = new List<Customers>();
for (int i=0;i<dt.Rows.Count;i++)
{
Customer customer = new Customer();
customer.CustomerID = Convert.ToInt32(dt.Rows[i][0]);
customer.CustomerName = dt.Rows[i][1].ToString();
customer.CustomerSurname = dt.Rows[i][2].ToString();
customers.Add(customer);
}
}
}
You need to do data binding only once not on every new record. That's the whole point of binding data to a control that all changes will be auto reflected.
Make customers a data member of the CustomerService class and change your code appropriately:
class CustomerService {
List<Customers> customers;
...
}
Next the binding code should be done once, maybe when the first record is added to the List<Customers>.
BindingSource binding = new BindingSource();
binding.DataSource = customers;
dataGridView1.DataSource = binding;
If you have done this right, every time any record is deleted or added to the customers collection, it should be automatically be reflected in the DataGridView control.
I think you must have used update panel and might not refreshing that
I think this will solve your problem, Check this........
DataGrid.CommitEdit();
DataGrid.Items.Refresh();
NOTE
CommitEdit() method will fire the RowEditEnding event, it is a endless loop. WPF does not allow to refresh the view while it is editing, since it may occur the endless loop. However, you could refresh the view after editing. Try to remove the RowEditEnding event handler (If Initialzed) , and do refresh for the Items; Then add the event handler back:
The following code is used to populate a DGV:
private void frmSwitch_Load(object sender, EventArgs e)
{
// TODO: This line of code loads data into the 'newCityCollectionDataSet.PropertyInformation' table. You can move, or remove it, as needed.
this.propertyInformationTableAdapter.Fill(this.newCityCollectionDataSet.PropertyInformation);
// TODO: This line of code loads data into the 'newCityCollectionDataSet.ClientTable' table. You can move, or remove it, as needed.
this.clientTableTableAdapter.Fill(this.newCityCollectionDataSet.ClientTable);
}
This code allows me to pass the necessary information to the "summary form":
private void propertyInformationDataGridView_CellContentDoubleClick(object sender, DataGridViewCellEventArgs e)
{
System.Data.DataRowView SelectedRowView;
newCityCollectionDataSet.PropertyInformationRow SelectedRow;
SelectedRowView = (System.Data.DataRowView)propertyInformationBindingSource.Current;
SelectedRow = (newCityCollectionDataSet.PropertyInformationRow)SelectedRowView.Row;
frmSummary SummaryForm = new frmSummary(this);
SummaryForm.LoadCaseNumberKey(SelectedRow.CaseNumberKey, true, null);
SummaryForm.LoadBRTNumberKey(SelectedRow.BRTNumber, null);
SummaryForm.Show();
}
What I am looking to do is pass the SelectedRow and add 1 to go to the next row if the current SelectedRow is no longer valid (for instance when FileFinishedCheckBox is checked on the "summary form"). I also want the same thing to happen anytime a checkbox is checked on the DataGridview so people do not have to scroll back to the file they are working on.
The code that performs the refresh whenever needed is as follows:
public void PerformRefresh()
{
this.propertyInformationBindingSource.EndEdit();
this.propertyInformationTableAdapter.Fill(this.newCityCollectionDataSet.PropertyInformation);
this.propertyInformationDataGridView.Refresh();
}
Any help would be great.
This question seems to be in two parts:
How to communicate between two windows forms
How to change the selected row in a datagridview
There are many different ways of achieving both tasks so I'm just going to give you two that will work. The first (for windows forms) is the simplest, while the second (for changing the selected row) is in my opinion the correct method.
Communication between windows forms
The most straightforward way to communicate between two windows forms is pass a reference to one form into the other form.
So say you have Form1 which opens Form2, you could do something like this:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
Form2 f = new Form2(this);
f.Show();
}
public void ShowMessage(string message)
{
MessageBox.Show(message);
}
}
public partial class Form2 : Form
{
private Form1 _parentForm;
public Form2(Form1 parentForm)
{
InitializeComponent();
_parentForm = parentForm;
_parentForm.ShowMessage("I am a message from form1);
}
}
So in your example you would add a method to the parent form which takes as its parameter the unique value for the row selected in dgv3 to show in gdv1. In the method (which is a member of the parentForm you put the centering code which I will show below).
Other ways of doing this include passing a delegate to the child form which is the method to center the datagridview. This has the advantage that you are no longer tied down to always passing in Form1 and can even provide different actions in resonse to the checkbox but is slightly more complicated to implement.
Centering on a selected record in a DataGridView
My preferred way of doing this is to use a bindingsource to provide the datasource for the grid. You can also directly access the grid position using the CurrentCell property but with the bindingsource you get a bit more bang for your buck.
In the code below we have a form which creates a BindingSource, sets its datasource to a BindingList of type MyBindingList and then sets the binding source as the datasource of a datagridview.
The objects within the BindingList have a unique property "PrimaryKey" allowing us to find them.
Then I show the centering code which is actually very simple.
First we get the index in the binding source of the desired you by calling the Find() method of the binding source.
Second we change the binding sources position (this also updates the datagridview display).
Finally we change the FirstDisplayedScrollingRowIndex of the datagridview so that the selected row is not at the very top or bottom of the grid (you will want to add a check to ensure this is a valid index if you use this line).
public partial class Form1 : Form
{
BindingSource bs;
public Form1()
{
InitializeComponent();
bs = new BindingSource();
MyBindingList<BackingObject> backing_objects = new MyBindingList<BackingObject>();
backing_objects.Add(new BackingObject{ PrimaryKey = 1, Name = "Fred", Hidden = "Fred 1"});
bs.DataSource = backing_objects;
dataGridView1.DataSource = bs;
}
private void button1_Click(object sender, EventArgs e)
{
int index = bs.Find("PrimaryKey", 5);
bs.Position = index;
dataGridView1.FirstDisplayedScrollingRowIndex = index - 1;
}
}
Now the last this to note is that bindinglist out of the box does not support the Find() method of the bindingsource. This is why I use my custom MyBindingList. Code to implement this can be found here.
Essentially you need a class like the following:
public class MyBindingList<T> : BindingList<T>
{
protected override bool SupportsSearchingCore
{
get
{
return true;
}
}
protected override int FindCore(PropertyDescriptor prop, object key)
{
// Get the property info for the specified property.
PropertyInfo propInfo = typeof(T).GetProperty(prop.Name);
T item;
if (key != null)
{
// Loop through the items to see if the key
// value matches the property value.
for (int i = 0; i < Count; ++i)
{
item = (T)Items[i];
if (propInfo.GetValue(item, null).Equals(key))
return i;
}
}
return -1;
}
}
I'm trying to bind separate ComboBox cells within a DataGridView to a custom class, and keep getting an error
DataGridViewComboBoxCell value is not valid
I'm currently assigning the data source for the cell to an IList<ICustomInterface> from a Dictionary I've got. Upon setting the data source however, the index for the ComboBoxCell isn't set, so it has an invalid value selected.
I'm trying to figure out how to get it to select a real value, e.g. the 0th item within the list it has been given to remove this error, or find another way to solve the problem. Anyone have any suggestions?
I managed to find the solution not long after posting the question. For anyone else:
The problem was that I was trying to assign the DataGridViewComboBoxCell.Value to an object, expecting that because the Cell was bound to a data source that it would automatically find the object in the source and update.
This wasn't actually the case, you actually need to set the value equal to that of the ValueMember property for it to correctly update the value and binding. I believe I was using a property 'Name' for both ValueMember and DisplayMember (controls how the renders in the cell) so setting the Value to interface.ToString() (rather than the interface instance) works for the majority of cases. Then I catch and ignore any DataError exceptions that occur while I'm changing the source around.
Here's my simple solution when using enums
ColumnType.ValueType = typeof (MyEnum);
ColumnType.DataSource = Enum.GetValues(typeof (MyEnum));
you can do that just after "InitializeComponent();"
Afters hours of trials, I finally found a solution that works.
// Create a DataGridView
System.Windows.Forms.DataGridView dgvCombo = new System.Windows.Forms.DataGridView();
// Create a DataGridViewComboBoxColumn
System.Windows.Forms.DataGridViewComboBoxColumn colCombo = new
System.Windows.Forms.DataGridViewComboBoxColumn();
// Add the DataGridViewComboBoxColumn to the DataGridView
dgvCombo.Columns.Add(colCombo);
// Define a data source somewhere, for instance:
public enum DataEnum
{
One,
Two,
Three
}
// Bind the DataGridViewComboBoxColumn to the data source, for instance:
colCombo.DataSource = Enum.GetNames(typeof(DataEnum));
// Create a DataGridViewRow:
DataGridViewRow row = new DataGridViewRow();
// Create a DataGridViewComboBoxCell:
DataGridViewComboBoxCell cellCombo = new DataGridViewComboBoxCell();
// Bind the DataGridViewComboBoxCell to the same data source as the DataGridViewComboBoxColumn:
cellCombo.DataSource = Enum.GetNames(typeof(DataEnum));
// Set the Value of the DataGridViewComboBoxCell to one of the values in the data source, for instance:
cellCombo.Value = "Two";
// (No need to set values for DisplayMember or ValueMember.)
// Add the DataGridViewComboBoxCell to the DataGridViewRow:
row.Cells.Add(cellCombo);
// Add the DataGridViewRow to the DataGridView:
dgvCombo.Rows.Add(row);
// To avoid all the annoying error messages, handle the DataError event of the DataGridView:
dgvCombo.DataError += new DataGridViewDataErrorEventHandler(dgvCombo_DataError);
void dgvCombo_DataError(object sender, DataGridViewDataErrorEventArgs e)
{
// (No need to write anything in here)
}
That is all.
I had the same problem.
In my case the solution was to fill the data adapter of the Foreign key table. This was not being automatically filled and this was the cause of the problem.
In the Page_Load Event:
Me.TblUserTypesTableAdapter.Fill(Me.DonateDataSet.tblUserTypes)
For the sake of people not struggling as much as i did.
When binding the combo you are setting a DisplayMember (what the user will see)
and ValueMember (what your application will get).
After setting up these you need to set up the Value and this is where it fails.
Basically the TYPE of the value needs to be the same TYPE as the ValueMember.
So if your value member is an ID obviously its of type INT and you need to set your value to int for example Cell.Value = 1;.
I am having the same problem. After populating my ComboBox column in the (unbouod) DataGrid, I solved my problem by setting the ValueMember property of the DataGridViewComboBoxColumn
Apparently, just relying on the ToString() property of the objects in the ComboBox is not enough.
Actual code:
/// <summary>
/// Populate the dataGridSplitVolumes data grid with all existing qualifications for the plan.
/// </summary>
/// <param name="bonus"></param>
private void PopulateDataGridSplitVolumes(Bonus_Group bonus)
{
try
{
List<Qualification> qualifications = Qualification.Load(this.groupBonus.PlanID, this.ConnectionString);
foreach (Qualification qual in qualifications)
{
DataGridViewComboBoxColumn col = (DataGridViewComboBoxColumn)this.dataGridSplitVolumes.Columns[0];
col.Items.Add(qual);
}
SplitVolumeGrid_QualificationColumn.ValueMember = "Name";
}
catch (Exception ex)
{
#if DEBUG
System.Diagnostics.Debugger.Break();
#endif
throw ex;
}
}//PopulateDataGridSplitVolumes
use DataError Event handler,
private void shahriartableDataGridView_DataError(object sender, DataGridViewDataErrorEventArgs e)
{
//You don't have to write anything here !
}
and your DisplayMember and ValueMember data type should be the same, in my case I have CountryName for both. Everything works fine for me...!!
Here's a complete example with a basic form and DataGridView added via the designer:
Setup and bindings:
private void Form1_Load(object sender, EventArgs e)
{
var colors = new List<Code>()
{
new Code() {Value= "R", Text = "Red"},
new Code() {Value= "G", Text = "Green"},
new Code() {Value= "B", Text = "Blue"}
};
var users = new List<User>()
{
new User() {Name = "Briana", FavoriteColor = "B"},
new User() {Name = "Grace", FavoriteColor = "G"}
};
var colorCol = new DataGridViewComboBoxColumn();
colorCol.DataSource = colors;
colorCol.DisplayMember = "Text";
colorCol.ValueMember = "Value";
colorCol.DataPropertyName = "FavoriteColor";
dataGridView1.Columns.Add(colorCol);
dataGridView1.DataSource = users;
}
Some classes:
public class Code
{
public string Value { get; set; }
public string Text { get; set; }
}
public class User
{
public string Name { get; set; }
public string FavoriteColor { get; set; }
}
Set a null value to the cell:
dataGridView.CurrentRow.Cells[NAME].Value = null;
I was having the same problem. The message was 100% spot on. The values for the combobox were like: Exact, StartsWith... and I was trying to set the value Exactă (not Exact). This was happening automatically as I was reading the DataTable for the DataGridView from an .xml file with DataTable.ReadXml(...). The values in the .xml file were off.