Making a Datatable's content appear in a DataGridView - vb.net

I am continuing my rewrite of an old VB6 into .Net as part of my .Net learning. I have managed to read a CSV file into a Datatable, and I can view it has populated properly using the 'datatable visualiser' when I look at 'dt', but it doesn't seem to populate the DataGridView control I am trying to tie it to. I have spent a couple of hours browsing other threads and trying things, but I cannot see what I am doing wrong. The DataGridView is on another form in the App, and I am reading the CSV file (a config file of sorts) from the main MDI form. When I show the child form containing the DataGridView it is always empty (it actually seems to have added the two datatable rows, but their content is all blank).
Public dt As New DataTable
Public ThisFilename As String = "c:\SitesDB.cfg"
Private Sub GetSitesDB()
Dim sr As New IO.StreamReader(ThisFilename)
Dim newline() As String = sr.ReadLine.Split(","c)
dt.Columns.AddRange({New DataColumn(newline(0)), New DataColumn(newline(1)), New DataColumn(newline(2)), New DataColumn(newline(3)),
New DataColumn(newline(4)), New DataColumn(newline(5)), New DataColumn(newline(6)), New DataColumn(newline(7)), New DataColumn(newline(8)),
New DataColumn(newline(9)), New DataColumn(newline(10)), New DataColumn(newline(11))})
While (Not sr.EndOfStream)
newline = sr.ReadLine.Split(","c)
Dim newrow As DataRow = dt.NewRow
newrow.ItemArray = {newline(0), newline(1), newline(2), newline(3), newline(4), newline(5), newline(6), newline(7), newline(8), newline(9), newline(10), newline(11)}
dt.Rows.Add(newrow)
End While
Form1.DataGridView1.DataSource = dt ' data can be seen here using datatable visualiser hovering over 'dt'
End Sub
Private Sub ToolStripButton1_Click(sender As Object, e As EventArgs) Handles ToolStripButton1.Click
Form1.Show()
End Sub
I have tried refreshing the DataGridView, to no avail. I am wondering if its something to do with Form1.Show() being actioned after the datatable is tied to the DataGridView?
But the following didn't help
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
' chkBit.Checked = False
' Dim cfgtext As String : cfgtext = ""
DataGridView1.DataSource = NAMS.dt
End Sub

Related

OnSelectedIndexChanged no working in aspx Page

i have this dropdownlist in my page
<asp:DropDownList ID="ddlselectTaxas" runat="server" AutoPostBack="True" CssClass="form-control" OnSelectedIndexChanged="ddlselectTaxas_SelectedIndexChanged"
EnableViewState="True" ViewStateMode="Enabled"></asp:DropDownList>
and this is the function in the vb file
Public Sub ddlselectTaxas_SelectedIndexChanged(sender As Object, e As EventArgs)
taxas.TryGetValue(ddlselectTaxas.SelectedItem.Text, valorTxt.Text)
valorTxt.Text = valorTxt.Text.Substring(0, valorTxt.Text.Length - 2)
End Sub
I want to change the value of a textbox (valorTxt) when I select a diferent value in the dropdownlist, but the function is not firing. I dont know why, i have zero experience with VB and asp pages. I apreciate your help. thanks in advance.
EDIT
here is the full code of the page
Public Class Emitir
Inherits System.Web.UI.Page
Public taxas As New Dictionary(Of String, String)
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If Not IsPostBack Then
CarregaValores()
valorTxt.ReadOnly = True
End If
End Sub
Private Sub CarregaValores()
Dim dt As DataTable
Dim objAcesso As New AcessoBD
Dim consulta As String = "SELECT TE.COD_TAXA, TE.DESC_TAXA, TV.VL_TAXA FROM TAXAS_EXPEDIENTE TE JOIN TAXAS_EXPEDIENTE_VALOR TV ON TE.COD_TAXA = TV.COD_TAXA"
dt = objAcesso.DataTable(consulta, CommandType.Text)
For Each linha As DataRow In dt.Rows
taxas.Add(linha.ItemArray(1), linha.ItemArray(2))
Next
valorTxt.Text = dt.Rows.Item(0).ItemArray(2)
valorTxt.Text = valorTxt.Text.Substring(0, valorTxt.Text.Length - 2)
ddlselectTaxas.DataTextField = "DESC_TAXA"
ddlselectTaxas.DataValueField = "COD_TAXA"
ddlselectTaxas.DataSource = dt
ddlselectTaxas.DataBind()
End Sub
Protected Sub ddlselectTaxas_SelectedIndexChanged(sender As Object, e As EventArgs)
taxas.TryGetValue(ddlselectTaxas.SelectedItem.Text, valorTxt.Text)
valorTxt.Text = valorTxt.Text.Substring(0, valorTxt.Text.Length - 2)
End Sub
End Class
You don't show your code to load up the drop list.
Remember, your page load code fires EVERY time on post back.
So, if in code on page load, you load up the drop list? You need to do this:
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If Not IsPostBack Then
LoadData()
LoadCal()
End If
End Sub
Sub LoadData()
Dim cmdSQL As New SqlCommand(
"SELECT ID,(FirstName + ' ' + LastName) as EmpName
FROM Employee ORDER BY FirstName")
lstEmployee.DataSource = MyRstP(cmdSQL)
lstEmployee.DataBind()
End Sub
Function MyRstP(cmdSQL As SqlCommand) As DataTable
Dim rstData As New DataTable
Using conn As New SqlConnection(My.Settings.TEST4)
Using cmdSQL
cmdSQL.Connection = conn
conn.Open()
rstData.Load(cmdSQL.ExecuteReader)
End Using
End Using
Return rstData
End Function
Now, in above, I was using a listbox, but they work the same as a drop down list (combo box).
but, if you do NOT place the loading up of data inside of the If Not IsPostBack?
Then what happens is you select a drop down, and say click on a button (or in your case autopostback = true, then the page load FIRES FIRST and THEN your code runs!!!
and, if your code re-loads the drop list? Then your selection is lost!!!
So, for just about ANY page you create, ALWAYS, but always use the is postback code stub in on-load - and thus your loading of controls data ONLY occurs one time. Since if you re-load every time, then you lose your changes.
Well, I am not sure of what was happening, but I remade my page and it is working now. something was blocking the event. Thanks for your help.

Form Loads slowly in vb.net

I am currently working in ERP project on vb.net. I want to load product data in a textbox on form load. I am using autocomplete method but having a data of around 26000 the form loads slowly for 4 mins. Is there any way to avoid this or is there any way to call this function in background when the application starts?
This is my autocomplete textbox code. It works fine but it hangs alot as of the data is so large.
Private Sub pn()
Try
con = Class1.dbconn
Dim dt As New DataTable
Dim ds As New DataSet
ds.Tables.Add(dt)
Dim da As New SqlDataAdapter("select [Part Name] from
Part_Master_Download$", con)
da.Fill(dt)
Dim r As DataRow
TextBox9.AutoCompleteCustomSource.Clear()
For Each r In dt.Rows
TextBox9.AutoCompleteCustomSource.Add(r.Item(0).ToString)
Next
con.Close()
Catch ex As Exception
MessageBox.Show(ex.Message)
End Try
End Sub
the properties of the textbox should be set to true for autocompletetextbox
Don't populate the AutoCompleteCustomSource in a loop. Populate an array first and then load the list in one go with a single call to AddRange:
Dim items = dt.Rows.Cast(Of DataRow)().
Select(Function(row) CStr(row(0)).
ToArray()
TextBox9.AutoCompleteCustomSource.AddRange(items)
You should find that that speeds things up considerably. If there's still a problem with performance, we can look a bit further.
EDIT: To prove my point, I just tested the following code:
Public Class Form1
Private timer As Stopwatch
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
timer = Stopwatch.StartNew()
Dim rng As New Random
Dim a = Convert.ToInt32("a"c)
Dim z = Convert.ToInt32("z"c)
Dim items = Enumerable.Range(1, 26000).Select(Function(n) Convert.ToChar(rng.Next(a, z + 1)).ToString())
For Each item In items
TextBox1.AutoCompleteCustomSource.Add(item)
Next
End Sub
Private Sub Form1_Shown(sender As Object, e As EventArgs) Handles Me.Shown
MessageBox.Show(timer.Elapsed.ToString())
End Sub
End Class
and the message displayed "00:03:08.3167858", i.e. just over three minutes to load the list. I then changed the Load event handler to this:
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
timer = Stopwatch.StartNew()
Dim rng As New Random
Dim a = Convert.ToInt32("a"c)
Dim z = Convert.ToInt32("z"c)
Dim items = Enumerable.Range(1, 26000).Select(Function(n) Convert.ToChar(rng.Next(a, z + 1)).ToString())
TextBox1.AutoCompleteCustomSource.AddRange(items.ToArray())
End Sub
so a single call to AddRange instead of calling Add in a loop, and the message was "00:00:00.0557427", i.e. just under 56 milliseconds. Is that better?
You can use paging to control the amount of returned data. Check this link for detailed examples.
Another way is to use (Task Async and await) so you won't lock your UI.
Here is a few last suggestions not related directly to the question but might help clean the code a bit:
you can skip the dataset and use the datatable directly, you don't need it for just one table.
Bind your results in the datatable to a datagridview or a combobox instead of iterating through your results and filling a textbox.
cheers

DataGridView is not showing first row after removing other rows

I am having a problem that I have been debugging for some days.
Some context, I have an application where I store the data (in the following code a list of class Expediente) in xml files using serialization. For displaying the data I deserialize it and then I create a datatable which I use as the datasource for the datagrid. I added a datagridcheckbox column to check the ones to delete.
In the datagrid I have a toolstrip button that when clicked it removes the checked -or checked in edit mode thus the line CType(dgvr.Cells(0).GetEditedFormattedValue(dgvr.Index, DataGridViewDataErrorContexts.Commit), Boolean) = True- elements from the datasource and persists the datasource to an xml.
The problem is that when deserializing the xml and loading it again to a datatable and binding the datagrid datasource to it, it removes the first row. If I close the application and open it, the first row appears.
I have debuged and in the datatable the correct numbers of rows appear. Moreover, I have narrowed the problem to the binding navigator, when it sets its binding source to the datable there the first row of the datagrid disappears.
I paste a simplified version of the code, I can provide more if needed.
Note that if in LoadGridExpedientes() I comment the lines
navExpedientes.BindingSource = Nothing
navExpedientes.BindingSource = bs
The first row doesn't disappear but obviously the counter of the binding navigator does not update.
Private Sub frmMain_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Dim chk As New DataGridViewCheckBoxColumn()
grdExpedientes.Columns.Add(chk)
chk.HeaderText = ""
chk.Name = "chk"
'fills a datatable from an xml, it works ok, it fills it with the correct amount of rows
Dim dt As DataTable = Sistema.getInstance.getDataExpedienteForGrid()
Dim bs As New BindingSource
bs.DataSource = dt
grdExpedientes.DataSource = bs
navExpedientes.BindingSource = bs
For i As Integer = 0 To grdExpedientes.Rows.Count - 2
grdExpedientes.Rows(i).Cells(0).Value = True
Next
End Sub
Private Sub LoadGridExpedientes()
grdExpedientes.DataSource = Nothing
navExpedientes.BindingSource = Nothing
grdExpedientes.Columns.Clear()
grdExpedientes.Rows.Clear()
'This is to know if there is already the checkbox column
If Not (grdExpedientes.Columns.Count > 0 AndAlso grdExpedientes.Columns(0).Name = "chk") Then
Dim chk As New DataGridViewCheckBoxColumn()
grdExpedientes.Columns.Add(chk)
chk.HeaderText = ""
chk.Name = "chk"
End If
Dim dt As DataTable = Sistema.getInstance.getDataExpedienteForGrid()
Dim bs As New BindingSource
bs.DataSource = dt
grdExpedientes.DataSource = bs
navExpedientes.BindingSource = bs
End Sub
Private Sub navExpedientesDelete_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles navExpedientesDelete.Click
For i As Integer = 0 To grdExpedientes.Rows.Count - 2
Dim dgvr As DataGridViewRow = grdExpedientes.Rows(i)
If CType(dgvr.Cells(0).Value, Boolean) = True Or _
CType(dgvr.Cells(0).GetEditedFormattedValue(dgvr.Index, DataGridViewDataErrorContexts.Commit), Boolean) = True Then
Dim draux As DataGridViewRow = dgvr
Dim expABorrar As Expediente = CType((From elem As Expediente In Sistema.listExpedientes
Where elem.Expediente = CType(draux.Cells("Expediente (Ficha)").Value, String)
Select elem).FirstOrDefault, Expediente)
Sistema.listExpedientes.Remove(expABorrar)
End If
Next
If System.IO.File.Exists(Sistema.pathListExpedientes) Then
System.IO.File.Delete(Sistema.pathListExpedientes)
End If
Dim sw As System.IO.TextWriter = New System.IO.StreamWriter(Sistema.rutaListaExpedientes, 0)
Serializer(Of Expediente).Serialize(Sistema.listExpedientes, sw, New List(Of Type) From {GetType(Movimiento)})
sw.Close()
LoadGridExpedientes()
End Sub
The problem was that I was using the delete toolstrip button that comes with the binding navigator, it seems that when clicked it has 'embedded' that it deletes the row that was selected that by default is the first one.
I created a new toolstrip button in the binding navigator and the problem was solved. I do not know why not updating the binding navigator binding source also avoided the first row from being removed.

Populate an ado.net dataset with only changes from the data source

This is so simple, I must be stupid!
I have a simple access database that a log record gets written to a few times an hour.
I'm trying to make a DataGridView that shows that data as it arrives.
My "Solution" is simple;
when a user clicks the view -> read from the database (fill the datatable) -> update the view.
Not what I dreamed of, but functional, if totally sub-optimal.
However, my "solution" is a dud, using fill draws every single record from the database, even if there are already 599 on screen.
Really, I just want fill the datatable once, and add new records as they arrive (or on click if needs be).
Bonus point if you can also explain another way (that isn't called so often) to hide the ID column, and change the header of column 1 (named DateTimeStamp) to TimeStamp.
Public Class FormMain
Shared dataAdapter As OleDbDataAdapter
Shared logTable As New DataTable("log")
Shared commandBuilder As OleDbCommandBuilder
Shared queryString As String = "SELECT * FROM log"
Shared bindingSource As New BindingSource
Private Sub FormServerBridge_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Try
ConfigureDataSet()
ConfigureBindingSource()
ConfigureDataView()
Catch ex As Exception
' FIXME: Helpful for debugging purposes but awful for the end-user.
MessageBox.Show(ex.Message)
End Try
End Sub
Private Sub ConfigureDataSet()
dataAdapter = New OleDbDataAdapter(queryString, _Config.ConnectionString)
commandBuilder = New OleDbCommandBuilder(dataAdapter)
commandBuilder.GetUpdateCommand()
dataAdapter.Fill(logTable)
With logTable
.Locale = System.Globalization.CultureInfo.InvariantCulture
.PrimaryKey = New DataColumn() {logTable.Columns("ID")}
End With
End Sub
Private Sub ConfigureBindingSource()
With bindingSource
.DataSource = logTable
End With
End Sub
Private Sub ConfigureDataView()
With DataGridView
.DataSource = bindingSource
End With
End Sub
Private Sub DataGridView_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles DataGridView.Click
UpdateUI()
End Sub
Sub UpdateUI()
dataAdapter.Fill(logTable)
End Sub
Private Sub DataGridView_DataBindingComplete(ByVal sender As Object, ByVal e As DataGridViewBindingCompleteEventArgs) Handles DataGridView.DataBindingComplete
' FIXME: This code gets run as many times as there are rows after dataAdapter.Fill!
With DataGridView
.Columns("ID").Visible = False
.Columns(1).HeaderText = "Timestamp"
.Columns(1).AutoSizeMode = DataGridViewAutoSizeColumnMode.AllCells
.Columns(2).AutoSizeMode = DataGridViewAutoSizeColumnMode.AllCells
.Columns(3).AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill
End With
End Sub
End Class
p.s. Links to websites and books will be appreciated, even the right MSDN page (if you know where it is, I admit I find it uncomfortable to peruse, I regularly get lost).
Assuming your IDs are sequential, the way I would approach this is:
1) Record the last id that you retrieved
2) When the user presses view, only get records whose IDs are greater than the last one record
3) Retrieve the records into a new datatable and then merge that with your existing data set.
Here is how I would make the changes (only changed info included):
Public Class FormMain
Shared logTable As DataTable
Shared bindingSource As New BindingSource
Private m_wLastID As Integer
Private Sub FormServerBridge_Load(ByVal sender As System.Object, ByVal e As System.EventArgs)
Try
ConfigureDataSet()
ConfigureBindingSource()
ConfigureDataView()
Catch ex As Exception
' FIXME: Helpful for debugging purposes but awful for the end-user.
MessageBox.Show(ex.Message)
End Try
End Sub
Private Sub ConfigureDataSet()
Dim queryString As String
queryString = "SELECT * FROM log WHERE ID > " & m_wLastID.ToString & " ORDER BY ID"
Using dataAdapter As New OleDbDataAdapter(queryString, _Config.ConnectionString)
Using commandBuilder As New OleDbCommandBuilder(dataAdapter)
Dim oDataTable As New DataTable("log")
commandBuilder.GetUpdateCommand()
dataAdapter.Fill(oDataTable)
With oDataTable
.Locale = System.Globalization.CultureInfo.InvariantCulture
.PrimaryKey = New DataColumn() {.Columns("ID")}
End With
' Record the last id
If oDataTable.Rows.Count <> 0 Then
m_wLastID = CInt(oDataTable.Rows(oDataTable.Rows.Count - 1)("ID"))
End If
If logTable Is Nothing Then
logTable = oDataTable
Else
logTable.Merge(oDataTable, True)
logTable.AcceptChanges()
End If
End Using
End Using
End Sub
Sub UpdateUI()
ConfigureDataSet()
End Sub
' Rest of the form code here

How to add a row to a datatable?

I face a stupid problem here. Cannot find an error and do not really know how to fix this.
I'm adding an item into datatable on selected index changed. Everything works, except only the last item is shown in dt. I always has the latest item and all others are gone. i suppose this is because I initialize it in the wrong place but still cannot find a solution.
Dim dt As DataTable = New DataTable()
Protected Sub Page_Load(sender As Object, e As System.EventArgs) Handles Page.Load
dt.Columns.Add("dr1")
dt.Columns.Add("dr2")
End Sub
Protected Sub ddlTest_SelectedIndexChanged(sender As Object, e As System.EventArgs) Handles ddlTest.SelectedIndexChanged
Dim row As DataRow = dt.NewRow()
row(0) = "Test!"
row(1) ="Test"
dt.Rows.Add(row)
End If
End Sub
This always shows only last item in list. How do I do this correct way?
Unless I am mistaken, you create a new DataTable every time a user hits the page.
You need to persist the row to a real database or perhaps make your DataTable shared (static in C#) instead.
Shared dt As DataTable = New DataTable()