How to add a row to a datatable? - vb.net

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()

Related

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 DataSource // integer to icon

I've got a list-based application that runs very slow, because each row in my DataGridView is build manually as DataGridViewRow. To fix performance issues i decided to use a DataSource (DataTable) instead.
My problem:
One column I am recieving from my database is filled with image IDs. The application knows which image belongs to which id. When creating gridrows manually there was no problem in translating int to a bitmap and use the bitmap as value for an imagecolumn. Now using the Datatable instead i cant get it working. For testing i wrote the following class:
Public Class test
Dim img As Bitmap
Public Sub New(image As Bitmap)
InitializeComponent()
Me.img = image
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim dt As New DataTable
dt.Columns.Add("StrCol")
dt.Columns.Add("ImgCol")
Dim row As DataRow = dt.NewRow
row.Item(0) = "Ohne Bild"
row.Item(1) = 0
Dim row2 As DataRow = dt.NewRow
row2.Item(0) = "Mit Bild"
row2.Item(1) = 1
dt.Rows.Add(row)
dt.Rows.Add(row2)
DataGridView1.DataSource = dt
End Sub
Private Sub DataGridView1_CellFormatting(sender As Object, e As DataGridViewCellFormattingEventArgs) Handles DataGridView1.CellFormatting
If e.ColumnIndex = 1 Then
If e.Value = 0 Then
e.Value = Nothing
Else
e.Value = img
End If
End If
End Sub
End Class
hint:
"Ohne Bild" translates into "Without image"
"Mit Bild" translates into "With image"
Output after Button1_Click()
EDIT
Due to a missunderstanding I will try to clarify my question;
By list-based i dont actually mean the list-object. My bad there.By list-based i mean an application that displays the user Database entrys he made. The very basic of my application is simple: Allow user to add Rows, Edit and delete rows in a datagridview. This information is saved and edited in a database. Some of these informations in the rows (2 columns to be exactly) are displayed as icons while the application simply saved an id for the icon in the database. So the application knows which ID belongs to which icon.
The sample dt in the question is just to have something to work with. In the application it will be data recieved from a database to a datatable.
I finally solved it myself.
I get a datatable from my database.
Then I add a Column to it with the DataType Bitmap
Then I loop through each row of the DataTable and translate the Image_ID column to the image itself and put the image into the new Column
Then I remove the Image_ID column
Then I use the new DataTable as DataSource
I created that code in my main application which is a lot more complex so I can't show the code here. Sorry for that and thanks for your help.
It is important to do every change you need to do before using the table as source, because as soon as the gridView has to change something that is displayed it takes a lot more time.

Making a Datatable's content appear in a DataGridView

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

Reading txt file line by line in DataGrid View

I'm completely new to VB.net and have been given a homework assignment. I need to be able to read certain lines and display them in a DataGridView. I have been able to link my .txt file to the DGV however it reads the whole file as opposed to the specific line. I have 4 buttons: btn1, btn2, btn3, btn4. I want each button to show the respective lines in the text file. After researching on-line for the past week I'm still stuck. If anyone could help me I would really appreciate it.
Text File ("database.txt")
(Line1) c1 c2 c3
(Line2) one 1-1 1-2
(Line3) two 2-2 2-3
(Line4) three 3-2 3-3
(Line5) four 4-2 4-3
Public Class Form1
Private Sub btn1_Click(sender As Object, e As EventArgs) Handles btn1.Click
Dim lines = (From line In IO.File.ReadAllLines("database.txt") _
Select line.Split(CChar(vbTab))).ToArray
For x As Integer = 0 To lines(0).GetUpperBound(0)
DataGridView1.Columns.Add(lines(0)(x), lines(0)(x))
Next
For x As Integer = 1 To lines.GetUpperBound(0)
DataGridView1.Rows.Add(lines(x))
Next
End Sub
End Class
You did not describe the result you get, so I cannot tell what's wrong right now, as I am not on a computer and usually use c++/cli (visual c++). If I have to make a guess, I would say that you fill each file row into one DGV row, but you want just 1 row, not all. Correct?
Anyway, here are some suggestions:
Do you have to read the file on each button click? If not, I would read it once, store the content and fill into the DGV when desired.
Debug your code! Set a breakpoint at the start of the function, step over the code lines, check the variable content.
Split up the code line dim lines into 3 or more separate lines to make it more readable. Also helps at debugging.
I made following assumptions based on what you had in your question:
First line of the file was the column names
The (Line#) was just there for reference and isn't in the actual file
The number in the button name (btn#) was the row index that should be displayed in the DGV
I added a new button to the form that loads the data from the text file, parses it to a datatable and sets the DataGridView1.DataSource to that DataTable. The second method then creates a new datatable and imports the specified row from the main datatable and shows it in the DGV. Of course many will say you should use a DataView with a DataView.RowFilter for this rather than creating a new datatable, however this way works just the same.
Private txtDataTable As DataTable
Private Sub loadFileBtn_Click(sender As Object, e As EventArgs) Handles loadFileBtn.Click
txtDataTable = New DataTable("txtContents")
Dim txtContents As String()
Try
txtContents = IO.File.ReadAllLines("C:\StackOverflow\database.txt")
Catch ex As Exception
MsgBox(ex.Message)
Return
End Try
Dim txtLines As New List(Of String())
txtContents.ToList().ForEach(Sub(x) txtLines.Add(x.Split(CChar(vbTab))))
If txtLines.Count > 0 Then
txtLines.Item(0).ToList.ForEach(Sub(x) txtDataTable.Columns.Add(New DataColumn(x.ToString)))
txtLines.RemoveAt(0)
End If
If txtLines.Count > 0 Then
txtLines.ToList.ForEach(Sub(x) txtDataTable.Rows.Add(x.ToArray))
End If
DataGridView1.DataSource = txtDataTable
End Sub
Private Sub btn_Click(sender As Object, e As EventArgs) Handles btn1.Click, btn2.Click, btn3.Click, btn4.Click
If txtDataTable Is Nothing Then Return
Dim rowIndex As Integer
If Integer.TryParse(DirectCast(sender, Button).Name.Replace("btn", String.Empty), rowIndex) Then
rowIndex -= 1
Else
Return
End If
Dim TempTable As DataTable = txtDataTable.Clone
If rowIndex < txtDataTable.Rows.Count Then
TempTable.ImportRow(txtDataTable.Rows(rowIndex))
End If
DataGridView1.DataSource = TempTable
End Sub

Assigning a DataTable to a ComboBox and then making changes

VB2010 I have created a DataTable manually so it does not come from a database. I have assigned it to a combobox and it displays my column of data. If I change the DataTable do I have to re-establish the link?
'assign first table
dt = GetFirstTable()
cbo.DataSource = dt
cbo.DisplayMember = "Time"
cbo.ValueMember = "Time"
'print out items in combobox
'assign second table
dt = GetSecondTable()
'cbo.DataSource = dt 'do i have to re-connect here?
'print out items in combobox
It seems if I do not re-establish the link I get the same items. I though since the cbo was already linked to the dt variable i didn't need to re-link it each time. Is that how that works or am I doing something wrong here?
When you assign cbo.DataSource = dt, and you then recreate dt, cbo.DataSource will keep pointing to the old table. This is pure pointer logic working here, same principle applies to all .NET code. It does not mean anything that you are re-using the same variable. You could have instead created dt2 and used that, behavior would be the same. So yes, if you recreate the DataTable, you need to reassign DataSource again. However, if you change the original dt, i.e. add rows, those will appear, so you will not need to reassign DataSource. Here is a code sample, to illustrate the approach:
Dim _dt As DataTable
Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
_dt = New DataTable
With _dt.Columns
.Add("key")
.Add("value")
End With
With ComboBox1
.DisplayMember = "value"
.ValueMember = "key"
.DataSource = _dt
End With
End Sub
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
_dt.Rows.Add({"item_key", "item_value"})
End Sub