Data Binding with `List<T>` - c#

I write a small program to record data change,it use a DataGridView,and it's datasource is a List, but I have a question on the DataBingding.
DataTable dataTable = GetBalance();
List<StockBalance> balances = ReadDataTable(dataTable);
List<StockBalance> stockBalances = (List<StockBalance>)dataGridView1.DataSource ?? new List<StockBalance>();
stockBalances.AddRange(balances);
dataGridView1.DataSource = stockBalances;
The above code can't refresh DataGridView, the data in balances will not show in DataGridView while stockBalances contains all new data, but the under code can archieveļ¼š
balances.AddRange(stockBalances);
dataGridView1.DataSource = balances;
I guess the reason is List and StockBalance is reference type,but I don't konw how to validate it, or it's not that?
Hope someone can help me, thanks.

A DataGridView - any bound control in fact - needs to receive a notification from the data source when the data changes in order to know that it needs to update. That requires an object that implements the IBindingList interface. The List<T> does not implement that interface and so the grid has no idea when data changes in the list and thus doesn't update.
What you should do is bind your list to a BindingSource and then bind that to the grid. In that case, when you make changes to the list you can then call an appropriate method of the BindingSource, e.g. ResetBindings, to provide an appropriate notification to the grid.
Note that, when I say "notification", I'm talking about an event. An IBindingList object raises its ListChanged event and the control handles that event.

You can do it using a Binding source:
var source = new BindingSource();
List<StockBalance> balances = ReadDataTable(dataTable);
List<StockBalance> stockBalances = (List<StockBalance>)dataGridView1.DataSource ?? new List<StockBalance>();
stockBalances.AddRange(balances);
source.DataSource = stockBalances;
dataGridView1.AutoGenerateColumns=true;
dataGridView1.DataSource = source;

Related

Showing the correct item in a master Combobox coupled with a BindingNavigator in a master/detail relationship

There are a lot of questions similar to mine but my problem is in the inverse of the typical question: I am unable to get the items in a master Combobox shows up correctly when moving along the detail recordset. I am quite new to Ado.Net so, please, expect some mess: my knowledge about the ADO.NET data object model is still poor; anyways this is my code:
private DataSet MainDataSet = new DataSet("MainDataSet");
BindingSource DetailBindingDataSource = new BindingSource();
BindingSource MasterBindingDataSource = new BindingSource();
SqlCeDataAdapter MasterDataAdapter;
SqlCeDataAdapter DetailDataAdapter;
MainDataSet.EnforceConstraints = true;
MasterDataAdapter = new SqlCeDataAdapter(GET_MASTERS_SQL_COMMAND, CONNECTION_STRING);
DetailDataAdapter = new SqlCeDataAdapter(GET_DETAILS_SQL_COMMAND, CONNECTION_STRING);
GetData(MainDataSet, ImagesDataAdapter, "masters"); // just a wrapper
GetData(MainDataSet, DefectsDataAdapter, "details"); // just a wrapper
DataTable DetailTable = MainDataSet.Tables["details"];
DataTable MasterTable = MainDataSet.Tables["masters"];
DetailBindingDataSource.DataSource = DetailTable;
MasterBindingDataSource.DataSource = MasterTable;
// establishing relationships between Master / Detail data
// to keep in sync related comboboxes with BindingNavigator
DataRelation DetailHasMaster = new DataRelation("DetailHasMaster", MainDataSet.Tables["masters"].Columns["master"], MainDataSet.Tables["details"].Columns["details"]);
MainDataSet.Relations.Add(DetailHasMaster);
BindingNavigator SearchNavigator = new BindingNavigator(true);
SearchNavigator.BindingSource = DetailBindingDataSource;
// Just as example, binding some fields: this is working, data change when moving on with BindingNavigator
DataTextBox.DataBindings.Add(new Binding("Text", DetailBindingDataSource, "creationDate"));
ItemTextBox.DataBindings.Add(new Binding("Text", DetailBindingDataSource, "partnumber"));
SerialNumTextBox.DataBindings.Add(new Binding("Text", DetailBindingDataSource, "SerialNumber"));
NoteTextBox.DataBindings.Add(new Binding("Text", DetailBindingDataSource, "note"));
/*
* Assign data origin to the binding sources;
* this is NOT working, the master combobox do not changes when moving
* on with BindingNavigator
*/
MasterComboBox.DataSource = MasterBindingDataSource;
MasterComboBox.DisplayMember = "name";
MasterComboBox.ValueMember = "master";
private void GetData(DataSet CurrentDataSet,
SqlCeDataAdapter CurrentDataAdapter,
String TableName)
{
CurrentDataAdapter.FillSchema(CurrentDataSet, SchemaType.Source, TableName);
CurrentDataAdapter.Fill(CurrentDataSet, TableName);
SqlCeCommandBuilder GenericCommandBuilder = new SqlCeCommandBuilder(CurrentDataAdapter);
CurrentDataAdapter.UpdateCommand = GenericCommandBuilder.GetUpdateCommand();
CurrentDataAdapter.InsertCommand = GenericCommandBuilder.GetInsertCommand();
}
Have someone pointers to the right solution?
Thanks
Not sure if this is a proper solution: my idea was that the combobox could be updated automatically by means of some property or method of the involved objects. The quick and dirty solution I have implemented is:
Subscribe the BindingSource.CurrentChanged event to be notified about stepping along the data
Check for: if (BindingSource.Count == 0)
Update manually the ComboBox.SelectedValue with values from the current row.
I believe that a better and more ADO capabilities - aware must exists, but this anyway saved my Sunday.

Can't refresh datagridview with bindingsource

Goal:
Once clicking on add or delete button, the datagridview should be refreshed with the latest data from document.
Problem:
The datagridview can't be refreshed
after making changes by deleting or
adding new data.
I'm using binding source that is linked with datagridview's datasource.
I tried everything with different solution and read advise from different forum but still I can't solve this problem.
I also tried using these syntax "BindingSource.ResetBindings(false)", "BindingSource.Refresh()" etc but no result.
Links below:
How to refresh a bindingsource
http://www.eggheadcafe.com/community/aspnet/2/10114324/datagridview-refresh-from-another-form.aspx
http://blogs.msdn.com/b/dchandnani/archive/2005/03/15/396387.aspx
http://bytes.com/topic/c-sharp/answers/812061-problem-refresh-datagridview
bSrcStock.DataSource = myProductrepository.GetAllProductList();
dgridStock.DataSource = null;
dgridStock.DataSource = bSrcStock;
bSrcStock.ResetBindings(true);
dgridStock.Columns[0].Width = 101;
dgridStock.Columns[1].Width = 65;
dgridStock.Columns[2].Width = 80;
dgridStock.Columns[3].Width = 120;
dgridStock.Columns[4].Width = 90;
I have faced this same issue and found out that the problem is with the initialization of the BindingSource inside a static constructor (The class was a singleton). Upon realizing this, I moved the code to the calling event and it finally worked without needing to assign null or call the clear method. Hope this helps.
No need to define the columns (unless you really want to...)
Then just call for the refreshDataGridView Method every time you add or remove something from your list...
public List<CustomItem> ciList = new List<CustomItem>();
CustomItem tempItem = new CustomItem();
tempItem.Name = "Test Name";
ciList.add(tempItem);
refreshDataGridView();
private void refreshDataGridView()
{
dataGridView1.DataSource = typeof(List<>);
dataGridView1.DataSource = ciList;
dataGridView1.AutoResizeColumns();
dataGridView1.Refresh();
}
You need a list that will inform the BindingSource when an item is added etc. Use a System.ComponentModel.BindingList for that.
Dim lisItems As New System.ComponentModel.BindingList(Of myObject)
Works Great! Only AddRange is missing so this takes care of that:
Private Sub AddRange(ByVal lis As List(Of myObject))
For Each itm In lis
lisItems.Add(itm)
Next
End Sub
https://learn.microsoft.com/en-us/dotnet/api/system.componentmodel.bindinglist-1?view=netframework-4.7.2

Updating my database

I am new with the subsonic, and I have a problem when trying to update the database from the sql server. I have created a gridview by still is not returning the updates results. can you please help me? its getting an error code on dc.AddMostaHse();
(Cannot implicity convert type 'void to 'object')
Here is the code being done of DataAccess.cs page
public void AddMostaHse()
{
Mosta.MostaHSE1 xx = new MostaHSE1();
xx.ID = 94;
xx.FunctionLocation = "lza94";
xx.acno = 12;
xx.Save();
}
Binding it with the gridview.
{
DataAccess dc = new DataAccess();
gvtest.DataSource = dc.AddMostaHse();
gvtest.DataBind();
}
This doesn't make much sense. Your gridview should be being bound from a Read operation. You are currently binding it to a Insert/Write operation based on what you have provided. You should probably be grabbing a collection of MostaHSE1() and displaying that in your gridview. The return type of your Read function should most likely be DataTable or DataSet.
Your AddMostHse1() appears it should work, but you want to target a different event off of the gridview to do this. Maybe RowEditEnding or some other event.
Your method AddMostaHse is returning void (no return). You cannot bind a datagrid to void. If you want to bind the datagrid to the object you just created in the method then change your method to:
public MostaHSE1 AddMostaHse() {
Mosta.MostaHSE1 xx = new MostaHSE1();
xx.ID = 94;
xx.FunctionLocation = "lza94";
xx.acno = 12;
xx.Save();
return xx;
}
It seems strange to me why you are binding one object to a datagrid (letting alone why would you bind a created object like that to a datagrid, I assume you are just testing), usually you bind a collection of objects..therefore this may not bring the result you want.
A more common candidate for your datagrid would be something like:
public IEnumerable<MostaHSE1> GetAllMostaHse() {
return Mosta.MostaHSE1.All();
}

listbox Refresh() in c#

int[] arr = int[100];
listBox1.DataSource = arr;
void ComboBox1SelectedIndexChanged(object sender, EventArgs e)
{
.....//some processes
listBox1.DataSource = null;
listBox1.DataSource = arr;
}
is not working,
also,
listBox1.Refresh(); is not working,
also,
listBox1.Update(); is not working,
i know i can use BindingList<T> but i have to work with only array.
can you help me how can i refresh listbox?
my first answer on stack exchange here.
C# .Net 4.0:
listBox1.DataSource = null;
listBox1.DataSource = names;
I noticed that setting the datasource for the first time, it refreshes.
When it's set, and you try set it to the same one again, it doesn't update.
So I made it null, set it to the same one, and it displayed correctly for me with this issue.
ListBox only updates its shown content when the object that is binded on dataSource notifys it own changes. the BindingSource object has an event called DataSourceChanged. when the Source is changed to a different object the Listbox will update itself.
Same thing when you bind a List. Nothing will happen if you change the List, because the List doesn't notify that it has been changed. There is a Simple solution for this Problem: use BindingList
http://msdn.microsoft.com/de-de/library/ms132679%28v=vs.110%29.aspx
the BindingList has the ListChanged Event is called every time when the List is changed (obviously). So the DataBindings of Windows.Form objects use events like ListChanged to update themselves. A simple List doesn't support this event.
SO if you want to work with a lot of Data Bindings you should know about:
http://msdn.microsoft.com/de-de/library/system.componentmodel.inotifypropertychanged%28v=vs.110%29.aspx
Managed to do just with
FirstListBox.DataContext = null;
FirstListBox.DataContext = App.ViewModel;
Simply loses link and get all the data back to it.
I inherited ListBox and added a public method calling RefreshItems() which does what we want. Already implemented and all. I dont know why they didnt put in a public method.
The problem might come from the ListBox SelectionMode.
For a reason that I don't know, the databinding does not work when SelectionMode is SelectionMode.None.
A workaround could be:
listBox.SelectionMode = SelectionMode.MultiExtended;
listBox.DataSource = myDatasource;
listBox.SelectionMode = SelectionMode.None;
Hope it helps.
well, without binding I only managed with:
this.Hide();
this.Show();
it redraws everything...
try the following
listBox1.DataBind()
Use BeginUpdate and EndUpdate, that should solve it.
No need to set the data source twice
listBox1.BeginUpdate();
listBox1.DataSource = myList;
listBox1.EndUpdate();
Windows forms to see changes especially on Listbox and other controls before load is finished is tricky.
To see data as its loaded use invalidate(); then Update();
Quick Answer
BindingSource bs = new BindingSource();
bs.DataSource = arr;
listbox1.DataSource = bs;
arr[0] = 100; //Do some value change
bs.ResetBindings(false); //Refresh the listbox
Explanation
As stated by Microsoft:
The BindingSource component serves two purposes. First, it provides a layer of indirection when binding the controls on a form to data. This is accomplished by binding the BindingSource component to your data source, and then binding the controls on your form to the BindingSource component. All further interaction with the data, including navigating, sorting, filtering, and updating, is accomplished with calls to the BindingSource component.
So, if you have multiple Controls binded with same data which changes frequently, it is suggested to use BindingSource rather than setting DataSource property of the Controls directly to an Array or List etc.
Moreover, with decompiling ListBox class which is inherited from ListControl in .net 4.6.2, the setter of property DataSource is:
if ((value != null) && (!(value is IList) && !(value is IListSource)))
{
throw new ArgumentException(System.Windows.Forms.SR.GetString("BadDataSourceForComplexBinding"));
}
if (this.dataSource != value)
{
try
{
this.SetDataConnection(value, this.displayMember, false);
}
catch
{
this.DisplayMember = "";
}
if (value == null)
{
this.DisplayMember = "";
}
}
We can see if the value is null the DisplayMember property will automaticly be set to empty. And when you reset a value, several initialization will be performed in SetDataConnection method which takes extra cost.
In contrast, calling BingdingSource.ResetBindings only fires an event:
public void ResetBindings(bool metadataChanged)
{
if (metadataChanged)
{
this.OnListChanged(new ListChangedEventArgs(ListChangedType.PropertyDescriptorChanged, null));
}
this.OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
}
Although I didn't find the handler in ListBox but I guess there should be less side effects. So I think the highest voted answer is not the best solution.

DataGridView does not display DataTable

I've got the following code which I think ought to be binding a DataTable to a DataGridView, but the DataGridView shows up empty. The DataTable definately has rows, so I assume that I am binding the DataSource incorrectly some how. Does anyone see what is wrong with this:
DataBase db = new DataBase(re.OutputDir+"\\Matches.db");
MatchDBReader reader = new MatchDBReader(db.NewConnection());
BindingSource bindingSource = new BindingSource();
bindingSource.DataSource = reader.GetDataTable();
this.dataGridView1.DataSource = bindingSource.DataSource;
The first line simply gets a handle to the DB that I'm pulling data from.
The next line is a provides a class for reading from that same db - in particular it exposes the GetDataTable method with returns the data table that I intend to put into the DataGridView.
The next line is uninteresting...
The 4th line attempts to grab the DataTable - QuickWatch indicates that this is working...
The final line is where I assume i've screwed up...my understanding is that this binds the DataTable to the DataGridView GUI, but nothing shows up.
Any thoughts?
Try binding the DataGridView directly to the BindingSource, and not the BindingSource's DataSource:
this.dataGridView1.DataSource = bindingSource;
You need to attach your BindingSource to your grid, before you get the data table.
Try switching the last two lines of code:
DataBase db = new DataBase(re.OutputDir+"\\Matches.db");
MatchDBReader reader = new MatchDBReader(db.NewConnection());
BindingSource bindingSource = new BindingSource();
this.dataGridView1.DataSource = bindingSource.DataSource;
bindingSource.DataSource = reader.GetDataTable();
Along with above solutions also fix the "bindingSource" datamember property. like:
bindingSource.DataMember = yourDataSet.DataTable;
I had the same problem with sql database and datagrid view. After a great deal of trouble I found out that I've forgot to set dataMember property of my binding source.
best of luck.
None of this worked for me, though it all seemed like good advice. What I ended up doing was the biggest, worst hack on earth. What I was hoping to accomplish was simply to load a DB table from a SQLite db and present it (read only, with sortable columns) in the DataGridView. The actual DB would be programmatically specified at runtime. I defined the DataSet by adding a DataGridView to the form and used the Wizards to statically define the DB connection string. Then I went into the Settings.Designer.cs file and added a set accessor to the DB Connection string property:
namespace FormatDetector.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "9.0.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
public static Settings Default {
get {
return defaultInstance;
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.SpecialSettingAttribute(global::System.Configuration.SpecialSetting.ConnectionString)]
[global::System.Configuration.DefaultSettingValueAttribute("data source=E:\\workspace\\Test\\Matches.db;useutf16encoding=True")]
public string MatchesConnectionString {
get {
return ((string)(this["MatchesConnectionString"]));
}
set
{
(this["MatchesConnectionString"]) = value;
}
}
}
}
This is a klugey hack, but it works. Suggestions about how to clean this mess up are more than welcome.
brian
DataGridView takes a DataTable as a basis. The DataTable Columns save their Type as a property.
If this type is an Interface all you would see are empty cells.

Categories

Resources