I am using a bindingsource in my windows forms application to populate some textboxes etc in my view. Binding works OK but how can I unsubscribe my bindingSource from from my object?
bindingSource.DataSource = new Foo();//OK
bindingSource.DataSource = null;//Not ok
If I try to unbind by setting data = null I get an exception:
System.ArgumentException : Cannot bind
to the property or column Bar on the
DataSource. Parameter name: dataMember
I don't want to remove all bindings to my controls (i have a lot) but would like to suspend binding as long as the bindingSource has no data....
I found a workaround like this bindingSource.DataSource = typeof(Foo); but is this THE way?
The typeof "workaround" is actually what the windows forms designer does when you set the BindingSource's DataSource in the PropertyGrid, and select a type from "Project data sources".
Look at the generated code in the *.designer.cs file for your form.
We use this "trick" in one of our products, and it has worked well for many years now.
Regards
I am not aware of a .Data property for the BindingSource object, but there is a .DataSource property, which can be set to null:
bindingSource.DataSource = null;
This releases the binding source from the data. However, looking at the reference for BindingSource.DataSource:
DataSource property List results
---------------------------- -------------------------------------------
null with DataMember set Not supported, raises ArgumentException.
If you're using a DataMember, you can't set the DataSource to null without an exception.
Unfortunately I don't know whether your workaround is a proper way of doing it, but at least now we know that you can't simply bind to null when a DataMember is set.
mrlucmorin gave you correct answer. It is working and it is the correct way of handling such situation.
However it won't quite work if your DataSource is of DataTable type. In such case you might want to play with bs.RaiseListChangedEvents = false; before nulling the BindingSource.DataSource, and set it to true after you assign new DataSource. Right after you set it to true, don't forget to reset the bindings with bs.ResetBindings(true);
Be aware that this might cause leaving your databound controls with 'old' data in them.
When using typeof as "empty" value for the DataSource, you can test for it like this:
private void BindingSource_DataSourceChanged(object sender, EventArgs e)
{
DataSource dataSource = ((BindingSource)sender).DataSource;
if (dataSource is Type t && t == typeof(MyModel))
{
lblEmpty.Visible = true;
pnlDetails.Visible = false;
}
else
{
lblEmpty.Visible = false;
pnlDetails.Visible = true;
}
}
This way you can conditionally hide or show an "empty" message in the UI, in a simple way.
Related
I have a datagridview in a windows form which contains some columns. And I want to hide the Ist column(CompanyID) through the code behind.
But Ist column is not hiding.
Have tried below 2 things :
dgvVendorDetails.Columns["CompanyID"].Visible = false;
And:
dgvVendorDetails.Columns[0].Visible = false;
I don't know the reason behind this. I have searched a lot but got no solution.
Both of these syntax are corrects and should work:
dgvVendorDetails.Columns["CompanyID"].Visible = false;
dgvVendorDetails.Columns[0].Visible = false;
My guess is that you are using the DataGridView.AutoGenerateColumns functionnality and even if you set the DataSource property, the DatagridView won't create columns until the grid is displayed.
So it's possible that:
you try to access columns that do not exist yet (but the code should raise an exception)
or you access valid columns, but they are replaced when the grid is bound again and so your code has no effect (probably your case since you do not mention an exception).
If so, the solution is to use the DataBindingComplete Event.
See also these related issues:
Why DataGridColumn not getting removed from DataGridView
Datagirdview and tabcontrol issue
Strange issue with a datagridview and a tabcontrol C#
DataGridView has no columns
EDIT
As #brikovich pointed out, another solution is not not use the AutoGenerated columns but create them and configure them at design time or at runtime.
This thread How to select visible columns in Datagridview bound to DataTable can help you to achieve this and/or make a choice between these two options.
Set autogenerate columns to false and then add each column one by one to the grid. Then set the column you don't want to see to visible = false. No need for code behind.
Try this:
VB.net:
Private Sub dgvVendorDetails_ColumnAdded(sender As Object, e As DataGridViewColumnEventArgs) Handles dgvVendorDetails.ColumnAdded
If e.Column.Name = "CompanyID" Then dgvVendorDetails.Columns("CompanyID").Visible = False
End Sub
C#:
private void dgvVendorDetails_ColumnAdded(object sender, DataGridViewColumnEventArgs e)
{
if (e.Column.Name == "CompanyID")
dgvVendorDetails.Columns("CompanyID").Visible = false;
}
I have an issue with DataBindings in C#.
I have a BindingSource with it's DataSource set to a DataRowView
I then use the binding source to set all the databinding for my controls.
Here is my code:
bsDataRecord.DataSource = myDataRow; //bsDataRecord is a BindingSource and myDataRow is a DataRowView
//Add Databinding to my controls
dateNextDate.DataBindings.Add("Value", bsDataRecord, "Next_Date", false, DataSourceUpdateMode.OnPropertyChanged); //DateTimePicker
textInformation.DataBindings.Add("Text", bsDataRecord, "Information", false); //TextBox
//more controls, etc
My databindings all work fine. So as I select the control and enter a value, myDataRow is updated.
My problems occurs when I try to set a control's value in code e.g.
textInformation.Text="Test";
When I do this myDataRow isn't updated. The only way I can get myDataRow to update is to give the control that I've updated focus.
Hope that makes sense!? Anybody got any ideas?
Thanks in advance.
I'm using c#.Net 4.0.
After problematically setting a property of the data source, you need to get the controls to reread their values from the datasource. Often a simple call to
dataBindingSource.ResetBindings(false); // false for value change not schema change
But you could also per control use this:
foreach (Binding b in myControl.DataBindings) b.ReadValue();
Binding.ReadValue() causes the control's bound property to be set to the data source value
Hope this helps!
If you update a (non-two-way)binding's target "by hand", that breaks the binding and it will not work anymore. This is normal behaviour.
So make your binding two-way to achieve what you want to do.
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 have a Winforms app with a BindingSource component and its DataSource is set to a DataSource that I created for a custom data object. Also on the form are several controls that are bound to various properties of the object exposed through the BindingSource. Many of these controls are comboboxes and will display values with a backing enum so I'm setting the DataSource for these controls like this:
comboBox1.DataSource = new BindingSource(Utility.ToList(typeof(DataObject.EnumValues)), null);
comboBox1.DisplayMember = "Value";
comboBox1.ValueMember = "Key";
This is all working well enough but I have two comboboxes which I need to be able to change at runtime to display other values (with a different back enum). In these two cases, I'm creating the initial bindings and datasource in code like this:
comboBox2.DataBindings.Add(new Binding("SelectedValue", this.bindingSource, "PropertyName1", true));
comboBox2.DataSource = new BindingSource(Utility.ToList(typeof(DataObject.FirstSetOfEnumValues)), null);
comboBox2.DisplayMember = "Value";
comboBox2.ValueMember = "Key";
...and then when I need comboBox2 to bind to and display different values, I do this:
comboBox2.DataBindings.Clear();
comboBox2.DataBindings.Add(new Binding("SelectedValue", this.bindingSource, "PropertyName2", true));
comboBox2.DataSource = null;
comboBox2.DataSource = new BindingSource(Utility.ToList(typeof(DataObject.SecondSetOfEnumValues)), null);
comboBox2.DisplayMember = "Value";
comboBox2.ValueMember = "Key";
As best I can tell, this is working properly, but it's ugly and there has got to be a much better way to do this, right? If you know what it is, I'd love to hear it! Thanks very much!
If this was a web form, I might suggest having ComboBox2 as two separate ComboBoxes and hide/show the one you need. Although I appreciate this isn't quite so easy for a WinForm project, unless you're using a flowing layout?
You could add a function to return your data source, based on your enumeration type... I don't think you need to re-set your DisplayMember and ValueMember properties after calling Clear() (but I may be wrong).
Other than that, I don't think you can simplify it much more. Although I'd be happy to hear if someone has a better solution :)
You don't need to bind the ComboBoxes to a new instance of BindingSource.
Bind your ComboBoxes to their respective BindingSources. This can be done either through the Windows Forms Designer, or manually in your own code. Make sure to keep a reference to the BindingSources if you do it in your code. If you use the Designer, then a member is added for you to your Form class.
Then when you want to display a different set of values, all you need to do is change the DataSource on the BindingSources, and the ComboBoxes will update accordingly.
Cheers
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