Freeze UI with Threads - sql

I have a timer to make a thread:
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
'ListBox2.Items.Add("Hilo")
hiloCertificador1 = New Thread(AddressOf crearObjeto1)
hiloCertificador1.IsBackground = True
hiloCertificador1.Start()
End Sub
Public Sub crearObjeto1()
UpdateList()
End Sub
Private Delegate Sub UpdateListDelegate()
Private Sub UpdateList()
If Me.InvokeRequired Then
Me.BeginInvoke(New UpdateListDelegate(AddressOf UpdateList))
Else
Dim conn As New SqlConnection(parametrosCon)
Dim cmd = New SqlCommand("SELECT TOP 1 * FROM COLA WHERE docentry < 8654 and enviado = 0", conn)
Dim da As New SqlClient.SqlDataAdapter(cmd)
cmd.Connection.Open()
da.SelectCommand = cmd
da.Fill(dataSet, "Query")
For Each fila As DataRow In dataSet.Tables(0).Rows
cmd = New SqlCommand("UPDATE COLA SET enviado = 1 WHERE DOCENTRY = (#docEntry) AND TIPO = (#tipodoc)", conn)
cmd.Parameters.AddWithValue("#docEntry", fila("docentry"))
cmd.Parameters.AddWithValue("#tipodoc", fila("tipo"))
cmd.ExecuteNonQuery()
Dim factura As New FacturaCerificacion(fila("docentry"), fila("tipo"))
Next
cmd.Connection.Close()
dataSet.Tables("Query").Clear()
End If
End Sub
The timer have a 4000 interval, but when a thread start freeze my UI, I think is because the process is so big or the querys but i need make it without freezing.

The comment is correct, and I will describe for you the issues hinted therein
Using a System.Windows.Forms.Timer
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
This will run on the UI thread, and is only appropriate when doing UI things (even then, I am not sure you can really make a case for it over System.Threading.Timer)
Creating a new System.Threading.Thread
hiloCertificador1 = New Thread(AddressOf crearObjeto1)
hiloCertificador1.IsBackground = True
hiloCertificador1.Start()
This now runs off the UI, and is the entire contents of the Timer.Tick. So you've ticked on the UI then created a new thread off the UI. This is very odd
Calling a Sub to call a Sub
Public Sub crearObjeto1()
UpdateList()
End Sub
Private Sub UpdateList()
' do stuff
End Sub
The redundancy should be self-evident
Doing non-UI stuff but following the Control.InvokeRequired/BeginInvoke pattern
Private Delegate Sub UpdateListDelegate()
Private Sub UpdateList()
If Me.InvokeRequired Then
Me.BeginInvoke(New UpdateListDelegate(AddressOf UpdateList))
Else
' looks like a bunch of non-UI stuff
End If
End Sub
This pattern is used for doing things on the UI but there seems to be no UI code in that block.
Not using Using to ensure proper disposal of IDisposable objects
Dim conn As New SqlConnection(parametrosCon)
Dim cmd = New SqlCommand("SELECT TOP 1 * FROM COLA WHERE docentry < 8654 and enviado = 0", conn)
Dim da As New SqlClient.SqlDataAdapter(cmd)
' do stuff
cmd.Connection.Close()
DataSet.Tables("Query").Clear()
Nongermane to your current issue, but also important to know.
Solution
So although it looks like a noble effort, you seem to be going back and forth between UI and not for no reason at all, or more accurately, creating issues where none existed with some over-engineering. The whole thing can be simplified with some minor changes
Use System.Threading.Timer
Dim Timer2 As New System.Threading.Timer(Sub() UpdateList(), Nothing, -1, -1)
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
' done in Form_Load if your Timer1 is Enabled in designer
' or can be done in a Button.Click, or however you had enabled Timer1
Timer2.Change(2000, 4000) ' this will enable Timer after 2 seconds, then will tick every 4 seconds
'Timer2.Change(-1, -1) ' this is how it's disabled
End Sub
Just call this method, and use Using to properly dispose your database objects. Added a Sub DoUiStuff() which is how the pattern is properly implemented
Private Sub UpdateList()
Timer2.Change(-1, -1)
Using conn As New SqlConnection(parametrosCon)
conn.Open()
Using cmd = New SqlCommand("SELECT TOP 1 * FROM COLA WHERE docentry < 8654 and enviado = 0", conn)
Using da As New SqlClient.SqlDataAdapter(cmd)
da.SelectCommand = cmd
da.Fill(DataSet, "Query")
For Each fila As DataRow In DataSet.Tables(0).Rows
Using cmdInner = New SqlCommand("UPDATE COLA SET enviado = 1 WHERE DOCENTRY = (#docEntry) AND TIPO = (#tipodoc)", conn)
cmd.Connection.Open()
cmd.Parameters.AddWithValue("#docEntry", fila("docentry"))
cmd.Parameters.AddWithValue("#tipodoc", fila("tipo"))
cmd.ExecuteNonQuery()
Dim factura As New FacturaCerificacion(fila("docentry"), fila("tipo"))
End Using
Next
End Using
End Using
DoUiStuff(arguments) ' for example, if you need to update a GridView
DataSet.Tables("Query").Clear()
End Using
End Sub
Private Sub DoUiStuff(arguments As Whatever)
If Me.InvokeRequired() Then
Me.Invoke(New Action(Of Whatever)(AddressOf DoUiStuff), arguments)
Else
' do UI stuff with arguments
End If
Timer2.Change(2000, -1)
End Sub
Finally, so I'm not contradicting myself, I'll add the Dispose method to dispose of the Timer. This Sub will be by default in your Form.Designer.vb file which you can leave there or move it to Form.vb once you add to it.
'Form overrides dispose to clean up the component list.
<System.Diagnostics.DebuggerNonUserCode()>
Protected Overrides Sub Dispose(ByVal disposing As Boolean)
Try
If disposing Then
components?.Dispose()
Timer2?.Dispose()
End If
Finally
MyBase.Dispose(disposing)
End Try
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.

How to update progressbar when retrieve data from database to datagridview VB.NET

i have VB.net code to retrieve data from SQL server (stored proc) to Datagridview (dgMC).
everything worked fine, but the progressbar1 not update. i'd like to progressbar1 shows update in percentage for user knows the status of data retrieve. the data is around 1000K.
Friend Delegate Sub SetDataSourceDelegate(table As DataTable)
Private Sub setDataSource(table As DataTable)
' Invoke method if required:
If Me.InvokeRequired Then
Me.Invoke(New SetDataSourceDelegate(AddressOf setDataSource), table)
Else
dgMC.DataSource = table
ProgressBar1.Visible = False
End If
End Sub
Private Sub loadTable()
Dim cnn As SqlConnection = GetConnection()
Dim cmdSearch As New SqlCommand("MC_Display", cnn)
cmdSearch.CommandType = CommandType.StoredProcedure
Try
cnn.Open()
Dim readerSearch = cmdSearch.ExecuteReader
If readerSearch.HasRows Then
Dim dt = New DataTable()
dt.Load(readerSearch)
setDataSource(dt)
Else
Me.Cursor = Cursors.Default
MsgBox("No Data Found.", MsgBoxStyle.Exclamation)
dgMC.DataSource = Nothing
End If
readerSearch.Close()
Catch ex As Exception
Throw ex
Finally
cnn.Close()
End Try
End Sub
Private Sub btnGoMC_Click(sender As Object, e As EventArgs) Handles btnGoMC.Click
ProgressBar1.Visible = True
ProgressBar1.Style = ProgressBarStyle.Marquee
Dim thread As New System.Threading.Thread(New System.Threading.ThreadStart(AddressOf loadTable))
thread.Start()
End Sub
To know exactly the number of data retrieve, you have to create a query with count(*) of your data.
Then, when you are retrieving data, you have to know which row is being retrieve, because you have to calculate the percentage.
And finally, you refresh your progressBar :
Dim percentage As Double = (currentRow / totalRows) * 100
ProgressBar.Value = Int32.Parse(Math.Truncate(percentage).ToString())
I hope it helps you
Since you're not performing anything that is quantifiable (you're issuing a sql query against a database, blocking processing for that thread until the database gives you the data you requested) you're not going to be able to update the progress bar with anything meaningful. In cases like this a Marquee scroll is usually sufficient. Also, you have the progress bar on the UI thread so it can be responsive and kick off a new thread to keep the UI thread from being blocked. At this moment you can't access the progress bar on the UI thread easily.
Some other thoughts though... If you have access to the Task Parallel Library, I would advise using that instead of creating a raw Thread and starting the process execution. You can utilize a type in the TPL called Task, which is an abstraction of Thread and takes care of some details you probably don't need to concern yourself with in this particular application/scenario. It also yields powerful asynchronous programming through the Async/Await paradigm in .NET 4.5. There is an excellent blog series by a guy named Stephen Cleary who has great expertise on this paradigm: Stephen Cleary Async/Await
A brief Example:
Private Sub btnGoMC_Click(sender As Object, e As EventArgs) Handles btnGoMC.Click
ProgressBar1.Visible = True
ProgressBar1.Style = ProgressBarStyle.Marquee
Dim thread As New System.Threading.Thread(New System.Threading.ThreadStart(AddressOf loadTable))
thread.Start()
End Sub
Can become:
`Private Async Sub btnGoMC_Click(sender As Object, e As EventArgs) Handles btnGoMC.Click
ProgressBar1.Visible = True
ProgressBar1.Style = ProgressBarStyle.Marquee
Await Task.Run(Sub() loadTable)
End Sub`

after using background worker also my application get stuck,,how i can resolve

I am working on windows form application :
in my form am filling my datagrid view in some interval.so some time my application getting stuck..so i used back ground worker and timer
in back ground worker i am calling my function to fill the my data grid view.i set Timer Interval as 10000. in background worker i given code like this:
Private Sub BackgroundWorker1_DoWork
Call Fetch_Info()
End Sub
in Timer click event i given code like thise:
If Not BackgroundWorker1.IsBusy Then
BackgroundWorker1.RunWorkerAsync()
End If
my Fetch_Info() function like this:
Dim cnt As Integer
Dim tbarcodedgv As String
Dim totaltbarcode As String
cnt = DGVall.RowCount
Dim tbar As String
Dim locTable As New DataTable
locTable.Columns.Add("carid", GetType(String))
If cnt > 0 Then
For i = 0 To cnt - 2
tbarcodedgv = DGVall.Rows(i).Cells(0).Value
locTable.Rows.Add(tbarcodedgv)
Next
End If
Dim flag As Boolean = False
Dim dcnt As Integer = DGVall.RowCount
Dim trid As Integer
Dim tbarcode As String
Dim keyloc As String
Dim cmd23 As New SqlCommand("IBS_fetchrequested", con.connect)
cmd23.CommandType = CommandType.StoredProcedure
cmd23.Parameters.Add("#tid", SqlDbType.Int).Value = tid
If cnt > 1 Then
Dim tvp1 As SqlParameter = cmd23.Parameters.AddWithValue("#Tbaroced", locTable)
tvp1.SqlDbType = SqlDbType.Structured
tvp1.TypeName = "dbo.TBarcode"
End If
dr = cmd23.ExecuteReader
While dr.Read
flag = False
tbarcode = dr("TBarcode")
If flag = False Then
If dr("keyloc") Is DBNull.Value Then
keyloc = ""
Else
keyloc = dr("keyloc")
End If
Dim row0 As String() = {tbarcode, keyloc, "", "Release"}
DGVall.Rows.Add(row0)
AxWindowsMediaPlayer1.URL = "C:\Beep.mp3"
End If
End While
dr.Close()
con.disconnect()
While your background worker runs in another thread than your GUI you are manipulating the Datagridview that's running in the GUI's thread. This should usually not work at all but it is probably the reason, why your GUI hangs while the BGW is running.
Try splitting the work: The time consuming fetching of data from the database is carried out in the Backgroundworker's DoWork event handler and you set the results as the e.Result value of the EventArgs variable in the DoWork function.
Then you handle the Backgroundworker's RunWorkerCompleted event and there you quickly update your datagridview with the results you set in the DoWork method. That way your GUI has nothing to do with the actual time consuming task and will only be affected by the quick update of your datagridview.
The code example for this is:
Public Class Form1
Private WithEvents LazyBGW As New System.ComponentModel.BackgroundWorker
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
'This code runs in the UI-Thread
LazyBGW.RunWorkerAsync()
End Sub
Private Sub LazyBGW_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles LazyBGW.DoWork
'This code runs in the BGW-Thread
Dim a As Integer = 0
For i = 1 To 5
a += 1
'I'm a lazy worker, so after this hard work I need to...
Threading.Thread.Sleep(1000) 'This locks up the BGW-Thread, not the UI-thread
Next
'Work is done, put results in the eventargs-variable for further processing
e.Result = a
End Sub
Private Sub LazyBGW_RunWorkerCompleted(sender As Object, e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles LazyBGW.RunWorkerCompleted
'This code runs in the UI-Thread
Dim results As Integer = CInt(e.Result) 'e.Result contains whatever you put into it in the DoWork() method
MessageBox.Show("Finally the worker is done and our result is: " & results.ToString)
End Sub
End Class

Open a modal form from a background thread to block UI thread without also blocking background thread

Maybe this is a simple question and I just don't know the correct search terms to find the answer, but my Google-fu has failed me on this one.
My vb.net application has a background thread that controls all the socket communication. Occasionally, I need this communication thread to open up a modal form to display a message and block UI interaction until the communication thread completes a series of tasks at which point, the communication thread will remove the modal form, allowing the user to continue interaction.
Currently, my communications class containing the background thread has two events, StartBlockingTask and EndBlockingTask. My main form has event listeners for these events that call like-named subs. They call code looking like this:
Private Delegate Sub BlockingDelegate(ByVal reason As String)
Private Sub StartBlockingTask(ByVal reason As String)
If Me.InvokeRequired Then
Dim del As New BlockingDelegate(AddressOf StartBlockingTask)
Me.Invoke(del, New Object() {reason})
Else
Try
_frmBlock.lblBlock.Text = reason
_frmBlock.ShowDialog()
Catch ex As Exception
'stuff
End Try
End If
End Sub
Private Sub EndBlockingTask()
If Me.InvokeRequired Then
Dim del As New BlockingDelegate(AddressOf EndBlockingTask)
Me.Invoke(del, New Object() {""})
Else
Try
If (Not _frmBlock Is Nothing) Then
_frmBlock.DialogResult = Windows.Forms.DialogResult.OK
End If
Catch ex As Exception
'stuff
End Try
End If
End Sub
This successfully blocks the UI from interaction, but it also blocks the communications thread so the EndBlockingTask event never actually gets raised. How can I open this modal dialog from the communications thread and allow the communications thread to still continue running?
Thanks in advance!
I disagree.
All that needs to be done is to change Invoke() to BeginInvoke() and you're golden.
This is because Invoke() is actually synchronous which causes it block until ShowDialog() resolves.
Using BeginInvoke() makes it asynchronous and allows the UI to be blocked while the thread continues:
Public Class Form1
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
If Not BackgroundWorker1.IsBusy Then
BackgroundWorker1.RunWorkerAsync()
End If
End Sub
Private Delegate Sub BlockingDelegate(ByVal reason As String)
Private Sub StartBlockingTask(ByVal reason As String)
If Me.InvokeRequired Then
Dim del As New BlockingDelegate(AddressOf StartBlockingTask)
Me.BeginInvoke(del, New Object() {reason})
Else
Try
_frmBlock.lblBlock.Text = reason
_frmBlock.ShowDialog()
Catch ex As Exception
'stuff
End Try
End If
End Sub
Private Sub EndBlockingTask()
If Me.InvokeRequired Then
Dim del As New BlockingDelegate(AddressOf EndBlockingTask)
Me.BeginInvoke(del, New Object() {""})
Else
Try
If (Not _frmBlock Is Nothing) Then
_frmBlock.DialogResult = Windows.Forms.DialogResult.OK
End If
Catch ex As Exception
'stuff
End Try
End If
End Sub
Private Sub BackgroundWorker1_DoWork(sender As System.Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
For i As Integer = 1 To 10
BackgroundWorker1.ReportProgress(i)
System.Threading.Thread.Sleep(1000)
If i = 4 Then
Dim del As New BlockingDelegate(AddressOf StartBlockingTask)
del("bada...")
ElseIf i = 7 Then
Dim del As New BlockingDelegate(AddressOf EndBlockingTask)
del("bing!")
End If
Next
End Sub
Private Sub BackgroundWorker1_ProgressChanged(sender As Object, e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
Label1.Text = e.ProgressPercentage
End Sub
End Class
You are calling the address from within the sub it is created in. The Address needs to be called from outside this sub.
Private Sub StartBlockingTask(ByVal reason As String)
If Me.InvokeRequired Then
Dim del As New BlockingDelegate(AddressOf StartBlockingTask)
Private Sub EndBlockingTask()
If Me.InvokeRequired Then
Dim del As New BlockingDelegate(AddressOf EndBlockingTask)
You need to create two delegates. One for StartBlockingTask and one for EndBlockingTask
This is an example from MSDN,
Delegate Sub MySubDelegate(ByVal x As Integer)
Protected Sub Test()
Dim c2 As New class2()
' Test the delegate.
c2.DelegateTest()
End Sub
Class class1
Sub Sub1(ByVal x As Integer)
MessageBox.Show("The value of x is: " & CStr(x))
End Sub
End Class
Class class2
Sub DelegateTest()
Dim c1 As Class1
Dim msd As MySubDelegate
c1 = New Class1()
' Create an instance of the delegate.
msd = AddressOf c1.Sub1
msd.Invoke(10) ' Call the method.
End Sub
End Class
http://msdn.microsoft.com/en-us/library/5t38cb9x(v=vs.71).aspx
Let me know if this helps.

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