I am not really sure what went wrong, i declared dt on top as a class variable then declare it as new datatable in fill function used in pageload but when i pressed buttonadd, dt is nothing error pops up.
Private dt As DataTable
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If Not Page.IsPostBack Then
fill()
End If
End Sub
Protected Sub fill()
dt = New DataTable
dt.Columns.Add("Status", GetType(String))
End Sub
Protected Sub btnadd_Click(sender As Object, e As EventArgs) Handles btnadd.Click
Dim R As DataRow = dt.NewRow
R("Status") = "Pending"
dt.Rows.Add(R)
'dt.Rows.Add("pending")
GridView1.DataSource = dt
GridView1.DataBind()
End Sub
The key concept is that web pages are "state-less".
That means for each event code stub, then the code is starting over from scratch.
And it means for each browser round trip, then the browser code starts from scratch EACH time. So, you only load up the data table one time (first time - this is GOOD!!!).
The problem of course, without a control on the web page. (say a data grid), or say a simple text box? Those controls survive that so called round trip. The MOST important concept, and if were going to learn ONE thing about asp.net web pages?
You must grasp the round trip concept, and the so called page state.
So, what happens in your case?
First time - page loads - code runs server side (often called code behind).
Your form instance class is created, your load event runs, you load up the table. The browser is THEN send down to the client side. The web page is just sitting here. Maybe you close the browser. Maybe you un-plug your computer. The server does not know, or care about this. In fact, the server does NOT even know the web page exists anymore!!!
So, your web page is sitting here, and you click on that button.
The web page is now sent up to the server, and the code behind starts running - but it STARTS FROM FRESH scratch each time!
So your form level variable called "data table" does NOT exist any more and does NOT have a value.
So, there are several solutions here. For starters, we need that "table" to persist and survive the rounds trip. As noted, most controls you drop into that page WILL keep their values. This is called the "viewstate". So, if we want that data table to survive and "exist" the next time the user does something? Then we need to save or persist that table variable.
Another way? You can use what is called the session(). Session() is a attempt and system to allow use to shove values into Session(). And the session() survives round trips.
Now, given your example?
Well, then our code would become this:
Private dt As DataTable
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If Not Page.IsPostBack Then
fill()
Session("MyCoolTable") = dt
else
' this is a page post back - re-load the active table into our
' forms level dt.
dt = Session("MyCoolTable")
end if
In a lot of cases, I would assume the table that drives the grid is a database. And thus I would add the row to the database, and then re-bind the data grid.
But the above use of session() will persist the dt table for you.
Related
I don't normally post on forums because I try to find information for myself, and ask as an absolute last resort. I've tried scouring the net for answers, but I'm only receiving about half of the answer I'm looking for.
I'm currently building an application that deals with state law. There's one combo box and one text box. One for the offense title, and one for the numerical code for that particular code section. So say if I select "Kidnapping", it prepopulates the text box below it with "11-5-77", for example.
The method I've been using for, oh, about the last hour now, is:
If AWOffenseTitle.Text = "Kidnapping" Then
AWCN.Text = "11-5-77"
ElseIf AWOffenseTitle.Text = "False Imprisonment" Then
AWCN.Text = "11-5-78"
With AWOffenseTitle being the combo box name, and AWCN being the text box name. While this has proved to work perfectly well so far, I'm sure you can imagine with hundreds of offense titles, this is going to take a ridiculously long time. Well, I finally found a spreadsheet with offense titles and their respective title codes. What I'm looking to do is create two text files within a folder in the local directory "Offenses". One with a vertical list of offenses, and one with a vertical list of offense code numbers that populate the same lines in each. What I'm looking to do is populate the combo box with the contents of text file one (which I can do already), but then selecting an offense title will read the second text file and display it's proper title code. That's what has me at a loss. I'm relatively well-versed with vb.NET, but I'm not an expert by any means.
I'm hoping someone here will be able to provide a code example and explain it to me line-by-line so I can gain a better understanding. I want to get more proficient with VB although it's not so popular anymore. I've been using VB since 6.0, but not on a regular basis. More on a sporadic project kind of basis.
I really appreciate any assistance anyone might be able to provide, and if you need more information, I'd be glad to answer any questions. I tried to be as thorough as I could.
Thank you in advance!
First, you need to retrieve your data. I demonstrated using an Sql Server database containing a table named Offenses with columns named OffenseTitle and OffenseCode. You will have to change this code to match your situation.
Private Function GetOffenseData() As DataTable
Dim dt As New DataTable
Using cn As New SqlConnection("Your connection string"),
cmd As New SqlCommand("Select OffenseTitle, OffenseCode From Offenses;")
cn.Open()
dt.Load(cmd.ExecuteReader)
End Using
Return dt
End Function
As the Form loads, set the properties of the ComboBox. DisplayMember matches the name of the title column and ValueMember is the name of the code column.
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim dt = GetOffenseData()
ComboBox1.DisplayMember = "OffenseTitle"
ComboBox1.ValueMember = "OffenseCode"
ComboBox1.DataSource = dt
End Sub
Then when the selected item in the combo changes, just set the .Text property of TextBox to the SelectedValue in the combo and your code appears.
Private Sub ComboBox1_SelectionChangeCommitted(sender As Object, e As EventArgs) Handles ComboBox1.SelectionChangeCommitted
TextBox1.Text = ComboBox1.SelectedValue.ToString
End Sub
There are other ways to do this if your data source is other than a database. Please advise if you need additional help.
In addition to HardCode's comment and Mary's detailed answer, I can only add an answer that's somewhere in between them.
It might be the case, that the information is not taken from a database, but from another source, like a text/data file or a web service. So it might be useful to create an abstraction for the data source you actually use.
First, I create a class or struct that will hold the data for each combo box item.
Class Offense
Public ReadOnly Property Title As String
Public ReadOnly Property Code As String
Public Sub New(title As String, code As String)
Me.Title = title
Me.Code = code
End Sub
End Class
Next, you need a method that retrieves a list of offenses that you can bind to your combo box. It's entirely up to you how you fill/fetch the offenses list. I have simply hard coded your two values here.
Private Function GetOffenseData() As List(Of Offense)
Dim offenses As New List(Of Offense)
offenses.Add(New Offense("Kidnapping", "11-5-77"))
offenses.Add(New Offense("False Imprisonment", "11-5-78"))
Return offenses
End Function
At a certain moment (probably in your form's Load event handler), you need to initialize your combo box. Just like Mary did, I use data binding.
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
AWOffenseTitle.DropDownStyle = ComboBoxStyle.DropDownList
AWCN.ReadOnly = True
AWOffenseTitle.DisplayMember = NameOf(Offense.Title)
AWOffenseTitle.ValueMember = NameOf(Offense.Code)
AWOffenseTitle.DataSource = GetOffenseData()
End Sub
Note that I use the NameOf operator to get the desired property names of the Offense class. If you ever decide to rename the properties of your Offense class, you will be able to easily detect where they are used, since the compiler will complain if your code still uses the wrong property names somewhere.
Finally, the app needs to react to combo box value changes, so that the text box will show the corresponding offense code. Mary used an event handler for the SelectionChangeCommitted event, but I use a handler for the SelectedIndexChanged event instead:
Private Sub AWOffenseTitle_SelectedIndexChanged(sender As Object, e As EventArgs) Handles AWOffenseTitle.SelectedIndexChanged
AWCN.Text = AWOffenseTitle.SelectedValue
End Sub
(Up to now, I was not aware of the SelectionChangeCommitted event of the ComboBox control. I will need to look into this event to see if it is actually a better choice for this scenario, but I found that the SelectedIndexChanged event does the job just fine, so for now I sticked with that event, since I am more familiar with it.)
I added my Enrollment system access Database, into my Enrollment System vb.net form, as a data source. The Database has 2 tables in it, the accountTable and studentEnrollmentInformation. I dragged The accountTable's details and data grid view into my form designer. The following code automatically appeared in the code designer:
Private Sub AccountTableBindingNavigatorSaveItem_Click(sender As Object, e As EventArgs)
Me.Validate()
Me.AccountTableBindingSource.EndEdit()
Me.TableAdapterManager.UpdateAll(Me.EnrollmentSystemDataBaseDataSet)
End Sub
Private Sub enrollmentSystem_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Me.AccountTableTableAdapter.Fill(Me.EnrollmentSystemDataBaseDataSet.accountTable)
End Sub
The following code works for updating the accounTableDataGridView but it does not work for studentEnrollmentInformationDataGridView so i manually created one
for studentEnrollmentInformation.
Function updateStudent()
Me.Validate()
Me.StudentEnrollmentInformationBindingSource.EndEdit()
Me.TableAdapterManager.UpdateAll(Me.EnrollmentSystemDataBaseDataSet)
Me.StudentEnrollmentInformationTableAdapter.Fill(Me.EnrollmentSystemDataBaseDataSet.studentEnrollmentInformation)
End Function
This is the function that contains the update code, that i manually created for updating the studentEnrollmentDataGridView. Adding new Row works fine but when i try to update studentEnrollmentDataGridView the texts in the table disappears and does not update/save. I also had function for updating the accountTableDataGridView which works fine.
Function update() 'THIS FUNCTION CONTAINS PRE-MADE CODE TO MAKE UPDATING SHORTER IN WRITING CODE.
Me.Validate()
Me.AccountTableBindingSource.EndEdit()
Me.TableAdapterManager.UpdateAll(Me.EnrollmentSystemDataBaseDataSet)
Me.AccountTableTableAdapter.Fill(Me.EnrollmentSystemDataBaseDataSet.accountTable)
End Function
My Question is how do i update multiple Tables in my system? Updating the other table works fine but the other is not.
In the original auto-generated code, this is the line that retrieves the data in the first place:
Private Sub enrollmentSystem_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Me.AccountTableTableAdapter.Fill(Me.EnrollmentSystemDataBaseDataSet.accountTable)
End Sub
When the form loads, the Account data is retrieved into a DataTable that is already bound. If you want to retrieve Student Enrollment data too, do it in the same place:
Private Sub enrollmentSystem_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Me.AccountTableTableAdapter.Fill(Me.EnrollmentSystemDataBaseDataSet.accountTable)
Me.StudentEnrollmentInformationTableAdapter.Fill(Me.EnrollmentSystemDataBaseDataSet.studentEnrollmentInformation)
End Sub
Now you're populating both bound DataTables when the form loads. When it comes to saving, you do the same thing, i.e. add the code to save the changes to the other DataTable where you already have the code to save the first:
Private Sub AccountTableBindingNavigatorSaveItem_Click(sender As Object, e As EventArgs)
Me.Validate()
Me.AccountTableBindingSource.EndEdit()
Me.StudentEnrollmentInformationBindingSource.EndEdit()
Me.TableAdapterManager.UpdateAll(Me.EnrollmentSystemDataBaseDataSet)
End Sub
You don't need any code to specifically save the changes from the DataTable because the whole point of UpdateAll is that it updates all DataTables in the DataSet.
As is always the case, if it doesn't seem to be working as you expect then you debug it. In that case, that would mean setting a breakpoint on the UpdateAll line and examining the exact state of the DataSet before and after the call, as well as possibly examing the sate of the database too.
This is a part of simultaneous url download program that i'm trying to make. It has the url list saved in a datatable named tbl and it is bound to a datagridview named dgvUrls. Evrytime it encounters a dead url, it removes it from the datatable.
I've reproduced the error using the code below. The Button3_Click adds 100 rows to the datatable, makes it as the datasource for datagridview. The q() removes the rows one at a time by removing the 1st row. The prob is that the datagridview don't reflect the changes made in the datatable
Dim tbl = New DataTable
Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
'Add 100 urls, for simplicity i'm adding only integers
tbl.Columns.Add("Urls")
For i = 1 To 100
tbl.Rows.Add(i)
Next
'bind to datagridview so that the end user can see the urls being download/removed from the list
dgvUrls.DataSource = tbl
'start multithread download , for simplicited (of this question) we have only one
Dim t As Thread = New Thread(AddressOf Download)
t.Start()
t.Join()
dgvUrls.Refresh()
End Sub
Private Sub download()
'for simplicity, the 1st 80 urls were dead!
For i = 1 To 80
'we remove the dead urls
tbl.Rows.RemoveAt(0)
Next
In general, it is a good thing to Refresh the DataGridView, mainly if you are performing the modifications from another thread; something like this:
Dim t As Thread = New Thread(AddressOf q)
t.Start()
t.Join() 'Waits for the other thread to complete, such that the next line is reached on the right moment
dgvUrls.Refresh()
I deleted Dim ts As ThreadStart = New ThreadStart(AddressOf q) because is not necessary. Also you don't need the Sleep and DoEvents:
Private Sub q()
For i = 1 To 98
tbl.Rows.RemoveAt(0)
Next
End Sub
As a proof of concept (to understand how all this works) is OK; but you should review various ideas in your logic before going further: removing so many rows from the DataSource can provoke problems (you would see that it triggers errors); ideally, (at least, I prefer it) you should modify the DataGridView directly (if possible) to avoid info-synchronisation problems; if you deal with multiple threads you would have to set up a "more proper structure" (the proposed t.Join() should be seen as a temporary fix to make this work).
I have a simple Windows form in VB: textbox bound thru an adapter and a bindingsource to my dataset.
I have a button that on Click I want it to update the database. The form loads and the first data row shows in the textbox, I change the text then click my button but no update happens.
Any ideas what I'm doing wrong, or how I should do this??
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Me.AToolsTableAdapter.Fill(Me.Qedsandb_TroyDataSet.aTools)
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
AToolsTableAdapter.Update(Qedsandb_TroyDataSet.aTools)
End Sub
End Class
Assuming the click event runs(?), TableAdapters based on a query (a join) do not, by default, have the ability to update the database. The name of your binding source suggests that you are using a query.
MSDN: TableAdapter Overview
The update functionality of a TableAdapter is dependent on how much
information is available based on the main query provided in the
TableAdapter Wizard. For example, TableAdapters that are configured to
fetch values from multiple tables (JOINs), scalar values, views, or
the results of aggregate functions are not initially created with the
ability to send updates back to the underlying database. However, you
can configure the INSERT, UPDATE and DELETE commands manually in the
Properties window.
You don't appear to be moving the data back from the form to the dataset. Try calling EndEdit on your bindingsource.
I have a program that inserts data in access database and the user can view the newly added items in a data grid view. After i add new items to the database it cannot be seen in the datagridview while the program is running. I have to stop the program and run it again just to see the changes i made.
Here is how i load the datagridview:
Public Class frmSupplies
Private Sub frmSupplies_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
'TODO: This line of code loads data into the 'SuppliesDataSet.product_info' table. You can move, or remove it, as needed.
Me.Product_infoTableAdapter.Fill(Me.SuppliesDataSet.product_info)
End Sub
How can i view the newly added items while the program is still running?
I created a module that will call the "refresh" function for the datagridview. I added a new module to my project and added this codes:
Imports System.Data.OleDb
Module Module1
Dim con As New OleDbConnection("Provider=Microsoft.Jet.Oledb.4.0;Data Source= database path")
Sub REFRESHDGV()
Dim sql As String
sql = "SELECT * FROM [product info]"
Dim adapter As New OleDbDataAdapter(sql, con)
Dim dt As New DataTable("product info")
adapter.Fill(dt)
Form1.dgv1.DataSource = dt
End Sub
End Module
Hope this helps others!
Try wrapping around beginedit ... end edit
gridview.BeginEdit();
----
gridview.EndEdit();
I am not familiar with access (I use oracle). Oracle database can notify you if something has changes in your database, so you can refresh the datagridview. In your case you can have a button (for refresh the datagridView) or a timer and refresh it every x time (you know the time):
'I imagine that you have a bindingSource
bindingSource1.DataSource = Product_infoTableAdapter.GetData()
bindingSource1.ResetBindings(false)
Maybe you want to take a look in DataGridView DataBinding
When you bind an Access table to a grid on a Windows form and then change the data outside that application, the form or the grid simply do not have a way of knowing that data was changed, unless 1) they receive some kind of notification from Access, or 2) the application keep querying the data source to see if changes were made.
Option 1 there is not applicable with MS Access.
Your only option is to put a timer on the form to regularly check for changes (on a fixed interval) and reload the table and refresh the grid if changes were found.