I have a ComboBox (Windows Forms) that is bound to a List. It is created at design time. When the List contents are changed my code calls a function to refresh the data binding. This works fine for .NET 3.5:
BindingData.SuspendBinding();
DataSource = null;
DataSource = BindingData;
BindingData.ResumeBinding();
I have switched to .NET 4.0 and it has stopped working. Specifically after stepping through this code the VS debugger shows BindingData.DataSource refers to a list with 127 items, but the ComboBox Items property contains zero items.
See this SO question along a similar theme: ComboBox Items Count Doesn't Match DataSource.
I have tried everything I can think of. Currently my code looks like the following and still doesn't work:
BindingData.SuspendBinding();
DataSource = null;
DataSource = BindingData;
BindingData.ResumeBinding();
BindingContext Dummy = this.BindingContext;
Invalidate();
PerformLayout();
I tried switching from List to BindingList and that didn't help. I had to switch from .NET 3.5 to .NET 4.0 against my will so this is pretty frustrating. I'm sure there is a specific sequence that works. Any ideas?
This is how I am attaching the data source to the ComboBox:
private BindingSource BindingData = new BindingSource();
BindingData.DataSource = Nodes;
DataSource = BindingData;
thanks, Andy
I solved it. I guess at some point I made what I thought was a minor change but actually wasn't. This code was moved from being called when the ComboBox is being displayed to when it was being created. It didn't yet have a handle and so the data binding cannot be refreshed.
I added another refresh of the databinding again in a ComboBox.HandleCreated event and it works.
thanks, Andy
Why you're suspending and resuming the BindingSource? If you just change your DataSource there will be no performance pitfalls.
According to How to: Bind a Windows Forms ComboBox or ListBox Control to Data you can use the ComboBox's DisplayMember property:
//Sample for C++ .NET:
List<String^>^ options = gcnew List<String^>();
options->Add("Option 1");
options->Add("Option 2");
comboBox.DataSource = options;
comboBox.DisplayMember = "Length";//this causes an DataSource update but the ComboBox would
//show an item's length instead of the item itself
comboBox.DisplayMember = ""; //reset -> the ComboBox calls each List item's ToString
//member
"Length" refers to a public property of the String class. Better would be a property that refers directly to the string's characters. The only remaining public property of String is Chars but I couldn't make it work. So we reset DisplayMember by comboBox.DisplayMember = "", causing the ComboBox to call each List item's (a String) ToString method => problem solved.
Other List entries than Strings can be handled by the ComboBox's properties DisplayMember and ValueMember (they also apply to other controls):
DisplayMember & ValueMember
Related
It was a looooooong time since I did a winform desktop application in .Net and I wanted to do some selections based on listboxes. The problem is that any item I select in a listbox also changes the selection of the other listboxes bound to the same data. I tried to find people who have had the same problem but couldn't find any so I guess I'm doing something fundamentally wrong. Anyone have any hints? See below for the basic code.
Class for handling data (to which I later use as DataSource in listboxes).
public class Registry
{
public ICollection<Data.Vertice> Vertices { get; set; } = new List<Data.Vertice>();
}
How I set the data source to a listbox
listBox1.DataSource = null;
listBox1.DataSource = _registry.Vertices;
listBox1.DisplayMember = "Title";
Reza Aghai Solved the problem by finding a a post which I had missed in my search.
Basically a single BindingManagerBase is created and reused if you use the same DataSource. This can be solved by creating a new DataSource by for example doing the following:
DataSource = yourList.ToList()
I am developing a small desktop app, and there are several drop-down lists (combobox-es) on my form. I populate a list of strings, which will be used as data source for all of them. Here is example from my Form.cs class:
List<string> datasource = new List<string>();
datasource.Add("string 1");
datasource.Add("string 2");
Then I set this list as a data source to several comboboxes:
cmbDataType1.DataSource = datasource;
cmbDataType2.DataSource = datasource;
This all happens in same method, which is called from the Form constructor.
Here is the strange part: after I change a selected value in one of them, the same value will be set in the other one. There are no SelectedIndexChange events set. I have messed up somewhere, but I cant put my finger where...
The behavior that you see is by design. When you bind the same object as the data source for multiple controls, all the controls share the same binding source.
If you explicitly assign a new binding source to each control, even while using the same data source, all controls will be unbound and will act independent of each other:
cmbDataType1.DataSource = new BindingSource(datasource, "");
cmbDataType2.DataSource = new BindingSource(datasource, "");
You should set a new BindingContext for the control before binding the dataSource the next time:
cmbDataType1.BindingContext = new BindingContext();
cmbDataType1.DataSource = datasource;
cmbDataType2.BindingContext = new BindingContext();
cmbDataType2.DataSource = datasource;
Since you are binding to the same exact datasource that is the expected behavior. You will want to change your binding to be a OneWay binding or use different objects if you don't want the selecteditem to change.
I am getting one error when I am trying to populate binding source. The exception is as follows;
System.IndexOutOfRangeException: Index 0 does not have a value.
at System.Windows.Forms.CurrencyManager.get_Item(Int32 index)
at System.Windows.Forms.DataGridView.DataGridViewDataConnection.GetError(Int32 rowIndex)
I am using generic list to fill binding source. The code looks like,
foreach (listItem)
{
BindingSource.Add(listItem);
}
I tried resetting the datasource property, but still the same issue.
Please help me to resolve this issue.
As far as I understand, you don't have to populate BindingSource, you just have to populate the list it's bound to. That's the whole idea of binding. You bind your control to the data using bindingsource.
And then
myBindingSource.DataSource = listItem;
will do it.
Also, instead of binding your datagridview to BindingSource and your BindingSource to list, you can just bind your datagridview to BindingList. It is similar to List, but also implements IBindingList interface (when you set the BindingList object to List, it will return an object implementing IBindingList, so it'll be very similar)
Sou you can do:
myDataGridView.DataSource = myBindingList;
If properties of items on myBindingList change, the result will be reflected on datagridview by default, if the collection changed (some things were added or deleted), you may refresh it using:
CurrencyManager cm = (CurrencyManager)this.myDataGridView.BindingContext[myBindingList];
if (cm != null)
{
cm.Refresh();
}
The problem has been solved by this code:
grdOrders.DataSource = null;
grdOrders.DataSource = this._controller.OrderActionData;
The error occurs when the list is no longer synchronized with the DataGridView.
You can manually refresh the bindings after the list has been changed to ensure that the bindings are synchronized again:
myBindingSource.CurrencyManager.Refresh();
I am shooting in the dark here but assuming that is pseudo code then you need to set the datasource of a UI element to the binding source. Also, it may be easier to just do something like this:
var binding = new BindingSource();
binding.DataSource = listItem;
DataGridView.DataSource = binding;
More info regarding BindingSource can be found here: http://msdn.microsoft.com/en-us/library/system.windows.forms.bindingsource.aspx
I just set ItemsBindingSource.DataSource = Nothing ("Items" is the name of the table) right before I close the form. So I have...
Private Sub btnExit_Click(sender As Object, e As EventArgs) Handles btnExit.Click
ItemsBindingSource.DataSource = Nothing
Me.Close()
End Sub
May not be right, but I don't get the error.
I'm working on a GUI that allows the user to manipulate xml files. I display the xml file in a datagridview organized neatly by columns through xml elements. I allow the user to add columns as an extention on my project. The column gets added to the dataset table, then updated to the datagridveiew that I use to display the xml file in. I've included the ability for the user to add a combobox column to select choices instead of entering them in constantly like.. true or false. However, that is where the problem lies. Saving a normal column was easy. The combobox column is being a pain.
I have a "save combobox column" to have it updated to the xml and a "save" button to save in a destination of the user's choice.
I've done some research and it seems like the combobox class has such a feature to gain access to the selecteditem in the combobox put in by the user.
Where we have:
ComboBox box = new ComboBox();
box.SelectedItem;
I tried applying this to the combobox column class but it does not have such a function. Thus, I cannot figure out how to directly obtain the value of the user's selected item. I tried experimenting with comboboxcell's as well, but that didn't lead me anywhere either. Both those classes I played around with do not have a... "selected item" function and even google does not have a solution for me. =( I've also tried using the cell.value, but it is "null" for some reason. Even when the user selects an item in the box, it doesn't get saved into the cell's value.
TLDR:
My question in short is, how, if possible, do you gain access to the comboboxcolumn cell's selected item? Additionally, how would you then ensure that the item value is saved in the cell?
Thanks in advance. I'm using .NET 3.5 SP1, through Visual Studio 2008 C#.
Sincerely,
tf.rz
The Control in a DataGridView is not a ComboBox, it is a DataGridViewComboBox and has different properties and methods. From MSDN
Unlike the ComboBox control, the DataGridViewComboBoxCell does not have SelectedIndex and SelectedValue properties. Instead, selecting a value from a drop-down list sets the cell Value property.
However, you mentioned that the Cell.Value is null for you. Well there may be another step you are missing according to the following article (How to: Access Objects in a Windows Forms DataGridViewComboBoxCell Drop-Down List).
You must set the DataGridViewComboBoxColumn.ValueMember or DataGridViewComboBoxCell.ValueMember property to the name of a property on your business object. When the user makes a selection, the indicated property of the business object sets the cell Value property.
If we have bound a datagridcomboboxcell with a different DisplayMember and ValueMember, like so:
dgcombocell.DisplayMember = "Name";
dgcombocell.ValueMember = "Id";
dgcombocell.DataSource = dataset1.Tables[0];
Then for getting SelectedText, and SelectedValue, we can write this code:
string SelectedText = Convert.ToString((DataGridView1.Rows[0].Cells["dgcombocell"] as DataGridViewComboBoxCell).FormattedValue.ToString());
int SelectedVal = Convert.ToInt32(DataGridView1.Rows[0].Cells["dgcombocell"].Value);
I hope it solves your problem.
Use this to get or set selected value:
object selectedValue = currentRow.Cells["comboboxColumnName"].Value
Don't forget to set DisplayMember and ValueMember for your DataGridViewComboBoxColumn
This is how it is done
DataGridViewComboBoxCell comboCell = (DataGridViewComboBoxCell)dgv.Rows[0].Cells[1];
MessageBox.Show(""+comboCell.Items.IndexOf(comboCell.Value));
A .Net combox is actually a composite control made up of a textbox and a dropdownlist. Use box.Text to get the currently displayed information.
EDIT: The row or the cell should have a .FindControl() method. You'll need to do something like:
Combobox box = (Combobox)(row.FindControl("[combobox ID]"));
string val = box.Text;
Basically, you're finding the control within its container (row or cell), then casting the control found as a combobox, then accessing its .Text property.
I use this:
private int GetDataGridViewComboBoxCellSelectedIndex(DataGridViewCell d)
{
return ((DataGridViewComboBoxCell)d).Items.IndexOf(d.Value);
}
I have two combo boxes in my form. i have binded these two with different database tables but with the same display member and some of data are repeated. When i change the index of first combo box ,the second combo box index also changed.
how to clear this problem. please any one give me the solution
Thanks
//dt1---> a b c d
//dt2---> x c a y
cmb_STable.SelectedIndex = -1;
cmb_STable.DisplayMember = "tablename";
cmb_STable.DataSource = dt1;
cmb_mTable.SelectedIndex = -1;
cmb_mTable.DisplayMember = "tablename";
cmb_mTable.DataSource = dt2;
// if cmb_mTable_SelectedIndexChanged with a
, the cmb_sTable also changed with a
Shot in the dark:
I assume you are using ADO.Net to databind the comboboxes. Depending on the API you will either have a bindingsource or a CurrencyManager in the background which has a copy of the databound list and a pointer to the current item.
My guess is that you have bound both lists to this datasource via the same CurrencyManager/BindingSource and moving it on one control moves it on the other.
You need to add a new BindingSource or currencymanager to stop this behaviour.
Your code still doesn't show everything.
It looks like there is a Databinding for the SelectedValue for both boxes. If they are bound to the same item (copy pasted?) , then that explains your problem.