Forgive me for this seemingly obvious question, but my searches have not revealed the answer.
I have a DataGridView bound to a DataTable. Although all rows are displayed in the grid, the table represented by the Grid's DataSource does not get updated after changing values in one or more DataGridView columns. Both grid and table contain a Primary Key. What am I missing here? Do I need to do something in the RowValidated event?
dgvCriticalContacts.AutoGenerateColumns = False
Dim ccRs As DataTable = Common.OpenRecordset("SELECT * FROM PhoneList")
dgvCriticalContacts.DataSource = ccRs
A DataGridView does not have the ability to automatically update the database you are getting your information from. While you can use a DataTable to give the DataGridView a data source, it also does not have the ability to update itself back to the database.
However, the .Net Framework does have a TableAdapter ( http://msdn.microsoft.com/en-us/library/bz9tthwx%28v=vs.90%29.aspx, 3.5 and earlier) or a DataAdapter ( http://msdn.microsoft.com/en-us/library/system.data.common.dataadapter%28v=vs.90%29.aspx, All versions) that can perform these functions.
My personal experience is that these adapters very quickly cause more problems than they solve if it is anything more a simple data structure. If it is anything more complex, you are better off creating a class for your rows and making a BindingList (or something similar) to allow the DataGridView to display them. You will have to implement your own change tracking and CRUD statements, but it's well worth it and will likely cause less of a headache in the long run.
Related
I have a dataview grid bound to a datasource at run time. The datasource is filled from an access database via a DataAdapter. The data fills and displays correctly, and updates to existing rows seem to work OK but I have two problems:
When I type something in a new row and then press return or switch to a different row, I want the DataAdapter to add that row then and there to the database so I can retrieve the Autonumber index of the new record from Access and use that to add an associated record in a different table (Entries, a many to many linking table). This isn't happening. In the RowLeave event I have adapter.Update(dsSentences) and then I check for the new row, but the RowCount doesn't reflect its presence even though the newly added data is visible in the grid, and the adapter.Update doesn't seem to have triggered the Insert query that I specified in the DataAdapter. So nothing is added.
(edit: OK, so the new row has not yet been added when this event is fired. Which event should I then use to commit the data and retrieve the Autonumber primary key for my new record? I've tried UserAddedRow but that one fires before you've entered any data into the new row.)
THe second problem is that I need to update the data independently and then have the grid reflect those changes. How do I do that? Is there some call that will force the grid to get the updated data from the DataAdapter via the Dataset? Any help would be much appreciated. I'm almost ready to dtop the whole idea of binding data and do it all through code, Data binfing is supposed to save time but I'm finding it labyrinthine and unpredictable.
FWIW here's the query I'm using to fill the grid:
PARAMETERS nIdCollection Long;
SELECT tblSentences.IdSentence, tblSentences.SentenceText, tblSentences.SentenceParsed, Not IsNull([tblSentences]![SentenceParsed]) AS HasParsed, Entries.IdEntry
FROM tblSentences INNER JOIN Entries ON tblSentences.IdSentence = Entries.IdSentence
WHERE (((Entries.IdCollection)=[nIdCollection]))
ORDER BY Entries.SortValue;
As you can see, it requires a record in Entries. After I've entered a new record in tblSentences, before there are any entries the IdEntry will be null assuming it shows up at all. That's why I need to intercept directly after the Insert, add the record to Entries and requery to keep everything in order. You could do it all in an SQL stored procedure but I have to use Access.
Edit: After a lot of googling I've come to the conclusion that what I'm trying to do = add a record to a table through an additional INSERT query apart from the one handled by the DataAdapter, every time a new row is added - simply can't be done if you are using data binding. I am going to have to delete all my code and start from scratch populating the grid through code (unbound). I think it's the only way to do what I want. I will leave this here as a warning to anyone else not to make my mistake of trying to use Data binding when your data is coming from more than one table. Bad mistake.
A have a volunteer timesheet data entry system which allows the volunteers to enter the times they have spent on various activities. I used the VB.net Designer to create the system (OK, I know now that that was not a good move!) so please don't ask me to show my code, most of it is generated by the Designer. My problem is this:
Each new record is assigned a negative number as a primary key when it is entered which is the way a dgv works with Access Automumber keys. I am executing the following statements in the RowValidating event when the row is valid.
a_dgv.EndEdit()
a_dgv.CommitEdit(DataGridViewDataErrorContexts.Commit)
Me.TimeSheets2BindingSource.EndEdit()
Me.TableAdapterManager.UpdateAll(Me.MembershipDataSet)
This code does not update the primary key value on the dgv although it does so in the Access table. If a user then attempts to delete or alter a record he has earlier added in the same session the update fails with a concurrency error. The only answer if have found to this problem is to refill the whole table. This is obviously not a desirable solution. Does anyone have a proven tested one?
I should probably mention that my table has two databound comboboxes
I was under the impression that a datagrid that is the result of a dataset from say Access does not show the PK values as -1, -2, -3.
If you created the disconnected dataset (or datatable) in code from a fill (pull data from Access), then each row normally does not show the PK.
However, regardless of the above, assuming you entered 5 rows, and now need to see the PK values?
You will during data entry in the grid should see this:
In above, I have added two rows. Your save code is somewhat like this:
tblHotels = DataGridView1.DataSource
rstDataReader.Update(tblHotels)
tblHotels.AcceptChanges()
That will send the data back to SQL server (or Access), and the autonumber PK 'ids are then generated. However, such changes are NOT pulled back into the dataset/datatable. In other words, the PK id's are generated in the database, but UNLESS you re-pull the data, you are not going to see the PK values.
You WILL have to re-pull the data. However, you can keep the current position of the grid, and re-fill the data like this:
rstDataReader.Update(tblHotels)
tblHotels.AcceptChanges()
Dim MyTop As Integer = DataGridView1.FirstDisplayedScrollingRowIndex
tblHotels.Clear()
rstDataReader.Fill(tblHotels)
DataGridView1.FirstDisplayedScrollingRowIndex = MyTop
And then you should see this:
The other way would be to send + update each row as you edit data, and then pull the PK, but obvious then you not be able to update the all your grid changes with a SAVE button, and thus of course no un-do ability.
I find the above that re-positions the top of the grid does not flicker. On the other hand, I suppose this could/would depend on how large the data set is (but then again, loading up a grid with too many rows is less then ideal).
So, as far as I can tell, you have to re-pull the dataset/datatable to get the new generated PK id's, or you have to save + pull for each row you edit. For a gridview with even several 100 rows, I don't see any flicker with the above code.
I'm still pretty green when it comes to vb.net and programming in general, so I apologize ahead of time if this is general knowledge.
Scenario, I am dynamically creating the control's bindings, filling a datatable with my SQL tables Schema. At this point I want to be able to fill in my form and have those values populate my datatable.
So far my solution is to add a new datarow and then assign values to each field. This seems a little redundant because I already have the fields bound. Is there a trick to filling a datatable with values from a bound control?
What I am doing for binding:
boundSource.DataSource = myDataSet
boundSource.DataMember = myDataTablesName
me.ClientNumber.DataBindings.Add(New Binding("Text",boundSource, "Client_Number")
...
The above works fine when I populate the datatable form SQL, but when I am trying to add a new record I have to add the various control.values/text to the datatable.
I am using a sqlDataadapter with a commandbuilder to generate the Insert, Update and Delete. I believe the relevant code is:
Me.ValidateChildren()
bounndSource.EndEdit()
curCon.Open()
daClientMaster.Update(ds, "CLIENT_MASTER")
dt.AcceptChanges()
I would assume if I have 4 controls of varying types (text, date, etc.) and they are bound to specific fields in a datatable, that when I start the application I am starting on a new row of the datatable. So when I enter the information into the controls and then use the dataadapter update for said datatable, it would know to Insert. and really this is where the problem may lie. I am in way over my head, with too many task to accomplish, I have no time to fully understand anything I am doing (or maybe that's just programming in general.) If I am lacking an understanding of Databinding, or anything else, guidance to the correct information would be appreciated.
Thanks in advance
PC
I want to populate a DataGridView Grid in my Form with Data from SQL Query or from some Table form Database. I want to know various possible ways to populate the DataGridView Grid.
Thanks in advance.
There are basically 3 ways to display data in a DataGridView
Create the rows manually in a loop: it's very inefficient if you have a lot of data
Use the DataGridView's virtual mode: the DGV only creates as many rows as can be displayed, and
dynamically changes their contents when the user scrolls. You need
to handle the CellValueNeeded event to provide the required data to
the DGV
Use databinding: that's by far the easiest way. You just fill a
DataTable with the data from the database using a DbDataAdapter, and
you assign this DataTable to the DGV's DataSource property. The DGV
can automatically create the columns (AutoGenerateColumns = true),
or you can create them manually (you must set the DataPropertyName
of the column to the name of the field you want to display). In
databound mode, the DGV works like in virtual mode except that it
takes care of fetching the data from the datasource, so you don't
have anything to do. It's very efficient even for a large number of
rows
Link.
I used the DataSet Designer to create FTWDataSet which holds the AlarmText table from a SQLExpress database. This far my form contains ONLY Datagridview1. The code below successfully shows the contents of the AlarmText table plus the one added checkbox column (which I will populate with display-only data, and is not an issue here).
Dim ta As New FTWDataSetTableAdapters.AlarmTextTableAdapter
Dim dt As New FTWDataSet.AlarmTextDataTable
ta.Fill(dt)
DataGridView1.DataSource = dt
'create a new Bool column in the datatable
dt.Columns.Add("NewCol", (New Boolean).GetType)
What else do I need to do to use the DataGridView to edit and save values in the AlarmText table?
Here's a brief MSDN walkthrough on this topic.
Some notes:
You shouldn't need a binding source to persist changes back to the database.
To make the table adapter accessible to other procedures in your form, make it a form-scoped (a.k.a. member) variable instead of method-scoped, as in your example.
If you created your Dataset using the Dataset Designer, and you're fetching your original data from anything more than a simple table or view, then your adapter won't know how to update anything in the original database. You have to manually configure the UPDATE command in this situation. For reference, see the TableAdapter Update Commands section in the above link.
I should also mention that I avoid TableAdapters in ADO.Net like the plague. In theory they're very convenient and powerful. In practice, many of them (especially the one for the Oracle provider) are buggy, and if they don't work exactly right you're totally screwed.
If you're pulling from just one underlying table, the adapter should work fine (so ignore my nihilistic advice for the time being). It may be that adding an additional column in code will breaks the adapter (since there's no corresponding column in the database table).