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()
Related
I have a DataGridView with DataSource set to a BindingSource control, that is bound to an ObservableList of entities.
I use this grid as a read only grid, for searching purposes.
When an item is selected, another BindingSource is updated with the current selected item.
No issues until here.
The issue is when creating a new item and I want to select the new item in the DataGrid, as after saving user is supposed to be able edit some data of the just created record.
How can I do that?
I can only provide the entity itself or the ID, but I can't figure out how to do that.
I'm not quite used working with Windows Forms, so I'm sure I'm missing some trivial solution.
You should bind the SelectedItem property of the grid to a property where you can set what entity you wish to be selected. Not sure but you might need to trigger a refresh on the grid because it might not automatically refresh it's View.
Ok, after some research and inspecting the "row" object, I've found this solution that perfectly meet my needs.
If anyone find any flaw with this approach, please let me know.
Once the new item is saved, I get the entity updated with the new primary key, then I call the following function:
private void SelectDataGridItem(Model.MyEntityType selectedItem)
{
foreach (DataGridViewRow row in MyDataGrid.Rows)
{
var boundItem = (Model.MyEntityType) row.DataBoundItem;
if (boundItem.Id == selectedItem.Id)
{
row.Selected = true;
break;
}
}
}
I have two ListBox in my winforms application, I assigne a datasource for both of them as follow:
private void MakeMeasurementUnits()
{
var units = new List<MeasurementUnit>
{
new MeasurementUnit {Name = "Current", SiUnit = "A"},
new MeasurementUnit {Name = "Voltage", SiUnit = "V"},
new MeasurementUnit {Name = "Time", SiUnit = "s"},
new MeasurementUnit {Name = "Temprature", SiUnit = "°C"}
};
lbxXunit.DataSource = units;
lbxYunit.DataSource = units;
}
The strange thing is (or maybe because it is my first time!!), in the form when I click on items of one of these lisboxes, the same item in the second listbox gets selected as well. Is this a default behaviour? how to prevent this? If this is default behaviour, what is useful about it?
I found the quick remedy to be making two different datasources (same thing with another name)
The listbox seems to cache the binding source. This is default behavior. If you want to avoid this, the easy way is to create a copy of the list to bind to the second data source:
lbxXunit.DataSource = units;
lbxYunit.DataSource = units.ToList();
This is useful when you have multiple views of the same data and want to synchronize the selection of these items.
Yes, this is normal behaviour. It happens because the ListView control uses a BindingSource object to track the currently selected item. (A List has no way to track a selected item without a BindingSource.)
By default, a DataSource in a WinForms control uses a BindingSource created for it by the WinForms system itself.
You can read more about the BindingSource at:
http://msdn.microsoft.com/en-us/library/system.windows.forms.bindingsource.aspx
There is an article here which might help too:
http://blogs.msdn.com/b/bethmassi/archive/2007/09/19/binding-multiple-comboboxes-to-the-same-datasource.aspx
The behavior you have noted is the default/correct behavior for winforms controls. You can achieve what you are after by setting a new BindingContext for your second listbox control without creating a copy of your data source.
BindingContext
This is correct behaviour. The datasource management in WindowsForms keeps track of the selected item on control and manipulates binded data too.
The resolution you've found already: is assign 2 different data sources objects to these controls.
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.
How do I add row data directly to a DataGrid Class?
I am using a free opensource class from a company that I will not name (even if it is RadiantQ) that I like a lot. It has this cool MuLtiColumnTreeList control that is a combination of a tree control and a datagrid. It comes with an example code that you can look at and everything. It is pretty cool. It is inherited from the DataGrid class.
The problem is that I am kind of new to databinding on this level and so I would like to just go ahead and write some code that forces the data that I have gathered from another class into the control.
So I looked online for how to do that for a DataGrid class and the information is not easily available. Can anyone help?
It seems that once the databinding is done, and if you change the data you have to rebind to the control. This is what was giving me difficulty before. So what I have to do is run some command like this:
this.mutlicoolgridview.ItemsSource = null;
this.mutlicoolgridview.ItemsSource = SampleData.GetSampleDataNew();
The problem I am having now is this. After running his command about one thousand times, I actually run out of memory. I think that doing this:
this.mutlicoolgridview.ItemsSource = null;
is not such a good idea. Is there a better command to do to free up the memory?
This is a similar looking crash:
[]
If you have a List of objects, you can copy them to a BindingList. Then you can use
dataGrid.ItemsSource = myBindingList;
To add rows to a DataGrid, you will need to, first, bind a DataSource to the DataGrid and, then, add rows to your DataSource.
Valid DataSources are:
A DataTable
A DataView
A DataSet
A DataViewManager
A single dimension array
Any component that implements the IListSource interface
Any component that implements the IList interface
Here's an Windows Form example of adding a row to a DataTable that is bound to the DataGrid:
public partial class Form1 : Form
{
// Instantiate the DataSource that will be bound to the DataGrid
DataSet dataSet = new DataSet("MyDataSet");
DataTable dataTable = new DataTable("MyDataTable");
public Form1()
{
InitializeComponent();
this.dataSet.Tables.Add(this.dataTable);
this.dataTable.Columns.Add(new DataColumn("Date"));
// Bind the DataTable to the DataGrid
this.dataGrid1.SetDataBinding(this.dataSet, "MyDataTable");
}
private void button1_Click(object sender, EventArgs e)
{
// When the user clicks the button, add a new row to the DataTable
DataRow dr = this.dataTable.NewRow();
dr["Date"] = DateTime.Now;
this.dataTable.Rows.Add(dr);
}
}
I recommend that you create a throw away project and play around with the DataGrid class to get familiar with the different ways that the DataGrid works with DataSources.
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