vb.net working with multiple controls added at runtime - vb.net

I am adding a tab page and datagridview to a Tab Control for every record in a datatable.
I would like to have a new Tab/DataGridView for each record (there will be ~3 for right now). I am declaring a new DataGridView D. How do I refer to these controls later?
I will want to do things like save changes in the datagridview to the database. Currently I can get the data on the screen and it looks good, but I believe I am not adding the DataGridView controls correctly because I keep re-using "D" as the control.
Dim dt As New DataTable
GetDataTable("SELECT * FROM aFeeTypes DescSeq", dt)
Dim i As Integer
'for each class in the datatable add a tab and a datagridview
For i = 0 To dt.Rows.Count - 1
Dim dr As DataRow
dr = dt.Rows(i)
Dim D = New DataGridView
D.Visible = True
Dim tp As New TabPage
tp.Name = "tp" & i
tp.Text = dr.Item("Desc2")
frmUI.tcFee.TabPages.Add(tp)
frmUI.tcFee.TabPages(i).Controls.Add(D)
dgv_Fill(D, "SELECT * FROM Fee WHERE ClassID=" & dr.Item("ClassID") & " ORDER BY Seq")
D.AutoResizeColumns()
D.Width = tp.Width
D.Height = tp.Height
Next i
this does not work:
With frmUI.Controls("D" & i)
.AutoResizeColumns()
.Width = tp.Width
.Height = tp.Height
End With

D is purely the variable name in the scope you are using it in.
You need to give the control a unique Name that yo can reference it by later.
The Name property can be used at run time to evaluate the object by
name rather than type and programmatic name. Because the Name property
returns a String type, it can be evaluated in case-style logic
statements (Select statement in Visual Basic, switch statement in
Visual C# and Visual C++).

I found a solution to the problem. The problem is that the new row has an autonumber ID.
When da.update(dt) occurs, the new row is inserted to the database. The database knows the new autonumber ID. However, the datatable does not.
For the datatable to know, the dataadapter is refilled. However, this causes the datatable to have all the old rows plus all the new rows and they all appear in the datagridview.
By clearing the old rows from the datatable, the fill refreshes all the data in the datatable and therefore refreshes the datagridview.
Public Sub dgv_AddRow(ByVal dgvName As String)
Dim dgv As DataGridView = colDataGridView.Item(dgvName)
Dim da As SqlDataAdapter = colDataAdapter.Item(dgvName)
Dim dr As DataRow = frmUI.allDataSet.Tables(dgvName).NewRow
'autopopulate the class id
dr("ClassID") = dgv.Parent.Tag
'add the new row
frmUI.allDataSet.Tables(dgvName).Rows.Add(dr)
'get the changed table
Dim dt As DataTable = frmUI.allDataSet.Tables(dgvName)
'inserts the new row to the database. concurrency violation
'will occur because datatable does not know the new Autonumber ID for the new row.
da.Update(dt)
'remove the datatable rows so they can be replaced using the adapter fill
'if rows are not cleared, following fill causes datatable to
'have existing rows plus all the rows again due to the fill
frmUI.allDataSet.Tables(dgvName).Rows.Clear()
'refill the adapter (refill the table)
'Everything you do to the underlying dataTable
'gets displayed instantly on the datagridview
da.Fill(frmUI.allDataSet, dgvName)
End Sub

Related

About datagrid-view in vb.net

net.I created a checkbox column in data grid-view and call the checked value.If i unchecked and checked another row it shows error.Need to know how to write loop concept for this.
Here's how you should have a datagridview that has a checkbox column, and how you should access it:
Dim dt as New DataTable
dt.Columns.Add("Name")
dt.Columns.Add("Age", GetType(integer))
dt.Columns.Add("IsActiveMember", GetType(Boolean))
dt.Rows.Add("Siva", 32 , True)
dt.Rows.Add("Siva2", 32 , False)
dt.Rows.Add("Siva3", 33 , True)
yourdatagridview.DataSource = dt
i.e. you create a datatable to hold your data, fill it and bind it. the datagridview will create the columns automatically.
When you want to retrieve the checked boxes:
For Each ro as DataRow in DirectCast(yourdatagridview.DataSource, DataTable).Rows
If DirectCast(ro("IsActiveMember"), Boolean) Then
Messageox.Show(ro("Name") & " is an active member")
Else
Messageox.Show(ro("Name") & " is not an active member")
End If
Next ro
If you get tired of directcasting all the time, add a new DataSet object to your project and use a stringly typed dataset

Database Lookup From ComboBox selection

I have a question about database values and how to determine the id of a value that has been changed by the user at some point.
As it is currently set up there is a combobox that is populated from a dataset, and subsequent text boxes whose text should be determined by the value chosen from that combobox.
So let's say for example you select 'Company A' from the combobox, I would like all the corresponding information from that company's row in the dataset to fill the textboxes (Name = Company A, Address = 123 ABC St., etc.,)
I am able to populate the combobox just fine. It is only however when I change the index of the combobox that this specific error occurs:
An unhandled exception of type 'System.Data.OleDb.OleDbException'
occurred in System.Data.dll
Additional information: Data type mismatch in criteria expression.
Here is the corresponding code:
Imports System.Data.OleDb
Public Class CustomerContact
Dim cn As New OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=|datadirectory|\CentralDatabase.accdb;")
Dim da As New OleDbDataAdapter()
Dim dt As New DataTable()
Private Sub CustomerContact_Load(sender As Object, e As EventArgs) Handles MyBase.Load
cn.Open()
da.SelectCommand = New OleDbCommand("select * from Customers", cn)
da.Fill(dt)
Dim r As DataRow
For Each r In dt.Rows
cboVendorName.Items.Add(r("Name").ToString)
cboVendorName.ValueMember = "ID"
Next
cn.Close()
End Sub
Private Sub cboVendorName_SelectedIndexChanged(sender As Object, e As EventArgs) Handles cboVendorName.SelectedIndexChanged
cn.Open()
da.SelectCommand = New OleDbCommand("select * from Customers WHERE id='" & cboVendorName.SelectedValue & "'", cn)
da.Fill(dt)
Dim r As DataRow
For Each r In dt.Rows
txtNewName.Text = "Name"
txtAddress.Text = "Address"
Next
cn.Close()
End Sub
The error is caught at Line 24 of this code, at the second da.Fill(dt) . Now obviously from the exception I know that I am sending in a wrong datatype into the OleDbCommand, unfortunately I am a novice when it comes to SQL commands such as this. Also please keep in mind that I can't even test the second For loop, the one that is supposed to fill the Customer information into textboxes (for convenience I only copied the first two textboxes, of which there are nine in total). I am think I could use an If statement to determine if the row has been read, and from there populate the textboxes, but I will jump that hurdle when I can reach it.
Any guidance or suggestions would be much appreciated. Again I am a novice at managing a database and the code in question pertains to the project my current internship is having me write for them.
Since you already have all the data from that table in a DataTable, you dont need to run a query at all. Setup in form load (if you must):
' form level object:
Private ignore As Boolean
Private dtCust As New DataTable
...
Dim SQL As String = "SELECT Id, Name, Address, City FROM Customer"
Using dbcon = GetACEConnection()
Using cmd As New OleDbCommand(SQL, dbcon)
dbcon.Open()
dtCust.Load(cmd.ExecuteReader)
End Using
End Using
' pk required for Rows.Find
ignore = True
dtCust.PrimaryKey = New DataColumn() {dtCust.Columns(0)}
cboCust.DataSource = dtCust
cboCust.DisplayMember = "Name"
cboCust.ValueMember = "Id"
ignore = False
The ignore flag will allow you to ignore the first change that fires as a result of the DataSource being set. This will fire before the Display and Value members are set.
Preliminary issues/changes:
Connections are meant to be created, used and disposed of. This is slightly less true of Access, but still a good practice. Rather than connection strings everywhere, the GetACEConnection method creates them for me. The code is in this answer.
In the interest of economy, rather than a DataAdapter just to fill the table, I used a reader
The Using statements create and dispose of the Command object as well. Generally, if an object has a Dispose method, put it in a Using block.
I spelled out the columns for the SQL. If you don't need all the columns, dont ask for them all. Specifying them also allows me to control the order (display order in a DGV, reference columns by index - dr(1) = ... - among other things).
The important thing is that rather than adding items to the cbo, I used that DataTable as the DataSource for the combo. ValueMember doesn't do anything without a DataSource - which is the core problem you had. There was no DataSource, so SelectedValue was always Nothing in the event.
Then in SelectedValueChanged event:
Private Sub cboCust_SelectedValueChanged(sender As Object,
e As EventArgs) Handles cboCust.SelectedValueChanged
' ignore changes during form load:
If ignore Then Exit Sub
Dim custId = Convert.ToInt32(cboCust.SelectedValue)
Dim dr = dtCust.Rows.Find(custId)
Console.WriteLine(dr("Id"))
Console.WriteLine(dr("Name"))
Console.WriteLine(dr("Address"))
End Sub
Using the selected value, I find the related row in the DataTable. Find returns that DataRow (or Nothing) so I can access all the other information. Result:
4
Gautier
sdfsdfsdf
Another alternative would be:
Dim rows = dtCust.Select(String.Format("Id={0}", custId))
This would return an array of DataRow matching the criteria. The String.Format is useful when the target column is text. This method would not require the PK definition above:
Dim rows = dtCust.Select(String.Format("Name='{0}'", searchText))
For more information see:
Using Statement
Connection Pooling
GetConnection() method aka GetACEConnection

datatable is empty vb.net

I'm not familiar with .net languages. but I tried to copy a datagridview rows to a datatable.when I use Watch on my datatable it has values but when i try to watch dataset,my datatable is empty. here is my code :
Dim dt As New DataTable("repTable")
For Each col As DataGridViewColumn In dgrMatchesExacutives.Columns
dt.Columns.Add(col.HeaderText)
Next
For Each row As DataGridViewRow In dgrMatchesExacutives.Rows
Dim dRow As DataRow = dt.NewRow()
For Each cell As DataGridViewCell In row.Cells
dRow(cell.ColumnIndex) = cell.Value
Next
dt.Rows.Add(dRow)
Next
If ds.Tables.Contains("repTable") Then
ds.Tables.Remove("repTable")
End If
ds.Tables.Add("repTable")
The Tables property of the DataSet is a DataTableCollection and you add items to this collection using the provided overloads of the Add method. But if you call the overload that receives a string you CREATE a NEW datatable (empty of course). The fact that you call the created datatable with the same name of the existing one has no relevance for the dataset.
If you have manually created a datatable and prepared it yourself with a schema and records then you need to use the overload that takes a DataTable
ds.Tables.Add(dt)

InvalidCastException was unhandled in VB.NET

Referring to this question: StackOverflowException was unhandled in VB.NET I decided to create a new question because I have a new error.
Unable to cast object of type 'System.Windows.Forms.BindingSource' to type 'System.Data.DataTable'.
CODE (in button click event):
' Copy rows from the first datagridview to the second datagridview that is data bound
' First copy the second datagridviews datasource into a new data table
Dim dt As DataTable = CType(frmEncodeDatabase.EncodingDataGridView.DataSource, DataTable).Copy
Dim dr As DataRow
' Loop through all rows of the first datagridview and copy them into the data table
For r As Int32 = 0 To Me.DataGridViewX1.Rows.Count - 1
If Me.DataGridViewX1.Rows(r).IsNewRow = False Then ' Do not copy the new row
dr = dt.NewRow
' Loop through all cells of the first datagridview and populate the data row
For c As Int32 = 0 To Me.DataGridViewX1.ColumnCount - 1
dr(c) = Me.DataGridViewX1.Rows(r).Cells(c).Value
Next
dt.Rows.Add(dr) ' Add the data row to the data table
End If
Next
Me.DataGridView2.DataSource = dt ' Rebind the second datagridview with the new table which has all the rows from both datagridviews
frmEncodeDatabase.show()
The error, which is in the image, is in dt As DataTable = CType(frmEncodeDatabase.EncodingDataGridView.DataSource, DataTable).Copy where the error is right now. How will I modify the code?
The error message is self-explainable.
"Unabled to cast object of type 'BindingSource' to type 'DataTable'."
The datassource of your datagridview is a BindingSource, so you need to cast to this type.
Dim bs As BindingSource = CType(frmEncodeDatabase.EncodingDataGridView.DataSource, BindingSource)
Assuming that the datasource of the bindingsource is a datatable:
Dim dt As DataTable = CType(bs.DataSource, DataTable)
If not, you'll get another cast exception, but now you how to fix it.

How to import csv file in Datagridview with certain columns and rows?

I'm new to VB.net and I don't know how to display certain columns and rows in datagridview that was imported from CSV file. My problem is I have many columns and all I want to display is 2 columns:
Name,Age,Mobile Number,ID number
Alex,18,09848484841,0010
George,19,02987654321,0020
Toni,17,09277470257,0030
How can I display only the Name & Age columns and its rows?
If you use a datatable you get the data structure and collection together. something like this:
Dim sr As New IO.StreamReader(filename)
Dim dt As New DataTable
Dim newline() As String = sr.ReadLine.Split(","c)
dt.Columns.AddRange({New DataColumn(newline(0)), _
New DataColumn(newline(1))})
While (Not sr.EndOfStream)
newline = sr.ReadLine.Split(","c)
Dim newrow As DataRow = dt.NewRow
newrow.ItemArray = {newline(0), newline(1)}
dt.Rows.Add(newrow)
End While
DataGridView1.DataSource = dt
Use a custom class with properties that match the data you want to store and make an instance of that class for each row of data use read, then have a List(Of {custom class}) to hold each object and the DGV's DataSource property can view the collection in the grid. The property names in the class will be used as the header.