autoscroll to bottom of multiline textbox being updated by backgroundworker - vb.net

I have a background worker control that is set to perform a task, and update a multiline text box on my main UI using a delegate procedure. this is all working perfectly, however once the updating scrolls off the bottom of the text box, the scroll bars appear, but the continuous refreshing causes the text box to stay locked at the top. Ideally, I would like the text box to auto-scroll itself to the bottom to show the latest entry in real-time. What would be the best way to implement this?
I have tried using the scrolltocaret() method, with and without a SelectionStart = txtlog.Text.Length command preceding it. perhaps I'm putting it in the wrong place?
some code samples below:
Delegate code:
Delegate Sub updateresults_delegate(ByVal textbox As TextBox, ByVal text As String)
Private Sub updatelog_threadsafe(ByVal textbox As TextBox, ByVal text As String)
If textbox.InvokeRequired Then
Dim mydelegate As New updateresults_delegate(AddressOf updatelog_threadsafe)
Me.Invoke(mydelegate, New Object() {textbox, text})
'Me.txtlog.SelectionStart = txtlog.Text.Length
'Me.txtlog.ScrollToCaret()
Else
textbox.Text = text
End If
End Sub
main backgroundworker activity:
For i As Integer = val1 To val2
'generate an IP address from split host parts and current value of i
host = s1(0) & "." & s1(1) & "." & s1(2) & "." & i
Try 'attempt to ping the IP
Dim reply As PingReply = pingsender.Send(host, timeoutval, buffer, options)
If reply.Status = IPStatus.Success Then
name = System.Net.Dns.GetHostEntry(host)'get DNS entry
resulttext += String.Format("{1} - {2}: reply: Bytes={3} time{4} TTL={5}{0}", vbCrLf, name.HostName, reply.Address.ToString, reply.Buffer.Length, getms(reply.RoundtripTime), reply.Options.Ttl) 'print out success text
Else
resulttext += String.Format(" {1}: Ping failed. {2}{0}", vbCrLf, host, reply.Status.ToString) 'print out fail text
End If
updatelog_threadsafe(txtlog, resulttext) 'send text to textbox
System.Threading.Thread.Sleep(1000)
Catch ex As Exception
End Try
Next
I guess my main question is: I'm pretty certain that the textbox.scrolltocaret() is the correct method to use for what I want, but where is the best place for me to put it? I've tried it in the delegate, the main backgroundworker, as well as before & after the runworkerasync() method. none of these worked, and now I'm stumped!

Try it this way:
'textbox.Text = text
textbox.AppendText(text)
The code you commented out wasn't running on the GUI thread, and as M Granja pointed out, AppendText will automatically scroll to the appended text in the box, so no need to call ScrollToCaret.

xxx.SetFocus ' xxx = the name of the textbox
SendKeys "^{END}" ' pop to last line

Related

Check if data exist in file

I need help. I want to check if user exists by entering their ic number and I want to display another rest of their data by using file in visual basic. Unfortunately, an error occurs while doing that. I need help. If the user exists, then It will display automatically name, email, address and so on but if a user doesn't exist, then it shows message box. Here I attached the image of the display screen and the code. Please help me. Thank you.
Public Class Form1
Private Sub btnSearch_Click(sender As Object, e As EventArgs) Handles btnSearch.Click
Dim userFile As String = "C:\Users\HP\Desktop\userdata.txt"
Dim inputFile As String
If System.IO.File.Exists(userFile) = True Then
Dim objReader As New System.IO.StreamReader(userFile)
Dim intIc As Integer
Dim intCount As Integer = 0
Dim strName As String
Dim strEmail As String
Dim intPhoneNum As String
Dim strAdd1 As String
Dim strAdd2 As String
Dim intPostcode As String
Dim strState As String
Do While objReader.Peek() <> -1
intIc(intCount) = Convert.ToInt64(objReader.ReadLine())
If (intIc(intCount).Convert.ToInt64(objReader.ReadLine())) Then
strName(intCount) = objReader.ReadLine()
strEmail(intCount) = objReader.ReadLine()
intPhoneNum(intCount) = Convert.ToInt32(objReader.ReadLine())
strAdd1(intCount) = objReader.ReadLine()
strAdd2(intCount) = objReader.ReadLine()
intPostcode(intCount) = Convert.ToInt32(objReader.ReadLine())
strState(intCount) = objReader.ReadLine()
lblName.Text = strName
lblEmail.Text = strEmail
lblNum.Text = intPhoneNum
lblAdd1.Text = strAdd1
lblAdd2.Text = strAdd2
lblPostcode.Text = intPostcode
lblState.Text = strState
objReader.Close()
Else
MessageBox.Show("User Does Not Exist")
End If
intCount = intCount + 1
Loop
Else
MessageBox.Show("File Does Not Exist")
End If
End Sub
End Class
Your task, the easy way:
make a new project
add a DataSet to this new project
open the DataSet, in the properties call it something sensible
Right click the surface, add a new datatable, name it Person
Right click the datatable, add a column, name it IC. Right click, add column, name it Name. Keep going until you added all the fields you want to track(email,phone,address1 etc)
save the DataSet
open the form
show the datasources window (view menu.. other windows)
expand the nodes til you can see Person
click the drop down next to Person, switch from DataGridview to Details
drag Person onto the form. Text boxes, labels etc appear. In the tray at the bottom more things appear
add a textbox to the form and call it searchTextBox
add a search button to the form, double click it, add this line of code to the click handler:
personBindingSource.Filter = '[ic] LIKE '" & searchTextBox.Text & "'"
If personBindingSource.Count = 0 Then MessageBox.Show("No records")
double click the form background to add a form load event handler, put this line of code:
If IO.File.Exists("data.xml") Then .ReadXml("data.xml")
switch back to designer, single click the form background and switch to event properties of the form, add a handler to the form closing event:
.WriteXml("data.xml")
That's it, you now have a program that will open, read and fill the DataSet with data from the data.xml file, it will search it when you type something in the ic box, the text boxes use databinding to show values automatically, and when you close the program it will save updates data. The only task now is to load the xml file with data.
When the textboxes were added to the form you should also have seen a bar appear across the top with some left/right controls in and a green plus. Click the green plus, type some data in, click it again, type more data. Navigating back, if you're adding new data, will commit the data. If you're looking at existing data, editing it then navigating will commit it
After you added some data, you can search for existing data using the search box. When you've searched for a single value it should be the only thing shown and the nav will show "1 of 1". To get back to the mode where all data is showing, put a single asterisk in the search box and hit search; it should show the number records in the top bar and you can scroll them with the arrows.
If you already have lots of data in a file, like you use in your question, you can read it in a loop (like you do in your question, except don't use that code exactly cos it has loads of errors) as a one time thing and assign it into the datatable, or you can manipulate it directly into being XML in a text editor. This is easy to do if you have a capable text editor but I'll not offer any particular advice on it in case you don't have a large amount of existing data. Ask a new question if you do

how to keep the scrollbar at the bottom visual basic

In my a form, I have a rich text box with vertical scrollbars. I have set it so that a line is read from a local text file, and added to the existing text in the text box. The following code makes it so that a line is read from the text file, there is 3 seconds of pause, and then another line is read and added to the existing text. This is done the emulate the feeling of a messenger application, so the 3 seconds of pause is absolutely necessary.
However, I have found that whenever a new line is added and there is enough text in the rich text box for the scrollbar to appear, the scrollbar will jump to the top. Every new line is added to the bottom, so this is extremely annoying, because it means the user is sent all the way up to the top of the text every single time there is a new line added.
Is there anything I can do to prevent this? i.e: locking the scrollbar to the bottom? Or having the scrollbar automatically scroll down to the bottom whenever there is a new line added?
If you need any pictures or further code, please let me know.
Dim i As Integer = 0
Call Pause(3)
RichTextBox1.Text = R.ReadLine()
Do Until i = lineCount
Call Pause(3)
RichTextBox1.Text = RichTextBox1.Text + vbCrLf + vbCrLf + R.ReadLine()
i = i + 1
Loop
The pause subroutine:
Public Sub Pause(ByVal seconds As Single)
Dim newDate As Date
newDate = DateAndTime.Now.AddSeconds(seconds)
While DateAndTime.Now.Second <> newDate.Second
Application.DoEvents()
End While
End Sub
After the TextChangedEvent you can try to set the caret to the last postiton and then use ScrollToCaret

Access Public Function for Maintain Combo Lists

I have over 40 combo controls in my application. I am working on developing a public function, to put in the not in list event of every combo. The goal is to have 1 continuous pop up form, which will open, if the user says they want to add a new value to the combo. The open form command will pass open args for the
record source
the control source for the 1 text box on the continuous form (generically the type)
the label for the control source.
I'm having some trouble getting it to pass the open args. I debug.print the parts of the args, I can split them, when they get passed (inconsistent results getting the open args to pass correctly, as I try to debug), and I cannot seem to set the record source for the pop up form correctly. I've tried doing 1 at a time, and still can't seem to get it.
This is the public function:
Option Explicit
Public Function TypeNotInList(ctl As Control, arg1 As String, arg2 As Variant, arg3 As String)
On Error GoTo Err_TypeNotInList
Dim Msg, Style, Title
'arg1 is the row source of the combo, to be passed as the recordsource for the frmAddTypeVal form
'arg2 is the control source of the combo, to be passed as the control source for the text box in the frmAddTypeVal form
'arg3 is the label of the combo, to be used for messages, and the label of the text box in the frmAddTypeVal form
Msg = "The " & arg3 & " you entered is not in the " & arg3 & " list, would you like to add it now?"
Style = vbYesNo
Title = "Type or listing must be maintained"
Response = MsgBox(Msg, Style, Title)
If Response = vbYes Then
ctl.Undo
DoCmd.OpenForm "frmAddTypeVal", acNormal, , , , acDialog, arg1 & "|" & arg2 & "|" & arg3
ctl.Requery
End If
Exit_TypeNotInList:
Exit Function
Err_TypeNotInList:
MsgBox Err.Description
Resume Exit_TypeNotInList
End Function
This is how I am calling it, in 1 combo's Not In List event:
Option Explicit
Private Sub FKAuditType_NotInList(NewData As String, Response As Integer)
Dim a1 As String
Dim a2 As String
Dim a3 As String
a1 = Me.FKTypeXYZ.RowSource
a2 = "txtXYZType"
a3 = Me.lblTypeXYZ.Caption
TypeNotInList Me.FKTypeXYZ, a1, a2, a3
Response = acDataErrContinue
End Sub
That should be calling the public function, and passing the 4 parameters.
This is the form load of the generic continuous pop-up form, called frmAddTypeVal:
Option Explicit
Private Sub Form_Load()
Dim VarArgs() As String
VarArgs = Split(Me.OpenArgs, "|")
Me.Form.RecordSource = VarArgs(0)
Me.txtType.ControlSource = VarArgs(1)
Me.lblType.Caption = VarArgs(2)
End Sub
When I run this as is, my debug.print (s) give me the following:
ctl = FKFKTypeXYZ
arg1 = SELECT tblXYZType.ID, tblXYZType.txtXYZType FROM tblXYZType ORDER BY tblXYZType.txtXYZType;
arg2 = FKXYZType
arg3 = XYZ Type
openargs =
I get each value, but the open args is null. What the heck, Beck?
Can anyone help guide this clueless coder? lol
Thanks!
I edited this to update the code, with the changes made. Now it's working! At least the first part of the process. The openargs get passed and the pop-up form works correctly. When I click close on that form, I'm back on the form with the notinlist combo. That resumes it's process and I get a message: The text you entered isn't an item in the list.
It know that's the default notinlist message. The thing is, the public function is supposed to handle this. It has, in the if Response = vbYes Then
ctl.undo
'undo trying to add a value that is not in the list yet
and then after the open form (which would involve the close of that form, I would think)
ctl.requery
'requery the combo, so the added value(s) can be seen
Anyone know how I can adjust this to prevent that message, but not just disable all warnings?
Thanks!
Got it! In the not in list, after I call the public function, I have to add:
Response = acDataErrContinue
This let's the default error message take a seat lol.
Thanks for all the help! This is going to make setting this up for every darn combo so much easier!!!!

VB.NET - Combobox goes blank when a button is clicked, databind doesn't update

Please forgive me, it has been some time since I've used VB.Net so this may be easy.
I have a simple login form with a text box, combobox and a button. The combobox is binded to a SQL database and displays the user names from Users table. The idea is that you select a name, type in a password, and you logon to a new form (This part works Great!). If you type a wrong password, the form pops up a message that says "invalid." For some reason at this stage, the combobox goes blank. The drop down now has empty spaces where names once were. Typing in a name with a password here results in object reference errors. I have tried everything to get the combo box to reset and searched the web but nothing works!!!
(I know this is not a secure login, this is just an easy example of binding data to a combo box and checking it against SQL table). Thanks for your help!
enter code here
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles BTN_Logon.Click
Try
Dim STR_User As String = Users_nameComboBox.SelectedValue
Dim STR_Pwd As String = TB_Pwd.Text
STR_User = STR_User.Trim
'MessageBox.Show(STR_User & "|" & STR_Pwd)'Used to verify this stage works
Dim numRecords As Int16 = UsersTableAdapter.FillByLogin(Me.Login_testDataSet.users, STR_User, STR_Pwd)
'MessageBox.Show(numRecords) 'Used to verify this stage works
If (numRecords > 0) Then
Dim DB_User As String = Me.Login_testDataSet.users(0).users_name.Trim
Dim DB_Pwd As String = Me.Login_testDataSet.users(0).users_pwd.Trim
'MessageBox.Show(DB_User & "|" & DB_Pwd)'Used to verify this stage works
If DB_User.Equals(STR_User) And DB_Pwd.Equals(STR_Pwd) Then
'User is authenticated for application
'This section works (removed to cleanup code)
Else
MessageBox.Show("ERROR" & STR_User & "|" & STR_Pwd) 'Shows that this stage works
'Need to figure out why combobox doesn't refill, using restart for now
'Application.Restart()
End If
End If
MessageBox.Show("ERROR" & STR_User & "|" & STR_Pwd) 'Shows that this stage works
'Need to figure out why combobox doesn't refill, using restart for now
'Application.Restart()
Catch ex As Exception
MessageBox.Show(ex.Message)
'Need to figure out why combobox doesn't refill, using restart for now
'Application.Restart()
End Try
End Sub
InitializeComponent
'Users_nameComboBox
Me.Users_nameComboBox.DataBindings.Add(New System.Windows.Forms.Binding("Text",
Me.UsersBindingSource, "users_name", True))
Me.Users_nameComboBox.DataBindings.Add(New System.Windows.Forms.Binding("SelectedValue", Me.UsersBindingSource, "users_name", True))
Me.Users_nameComboBox.DataSource = Me.UsersBindingSource
Me.Users_nameComboBox.DisplayMember = "users_name"
Me.Users_nameComboBox.Name = "Users_nameComboBox"
Me.Users_nameComboBox.ValueMember = "users_name"

Overwrite Visual Basic label text

I am reading a temperature value from a transceiver into the serial port and I want this value to change the value of a label in my Visual Basic Form. This value changes every few seconds. I am using the code below:
Me.dataReceived.Text &= [text]
where dataReceived is the label I am using and [text] is the data I am reading from the serial port. This results in the data being displayed but instead of overwriting the label, it writes the value after each other. (The data is appended). I tried to remove the & before the = but this did not work as nothing showed up. Any ideas on what I can do?
The code I am using is the following:
'To receive data into the text field
Private Sub SerialPort1_DataReceived(ByVal sender As Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived
While (SerialPort1.IsOpen)
ReceivedText(SerialPort1.ReadExisting()) 'This is called automatically every time data is received at the Serial Port
End While
End Sub
Private Sub ReceivedText(ByVal [text] As String)
Dim temperature As String
'This function compares the creating Thread's ID with the calling Thread's ID
If Me.dataReceived.InvokeRequired Then
Dim x As New SetTextCallback(AddressOf ReceivedText)
Me.Invoke(x, New Object() {(text)})
Else
'To output to the text box:
Me.dataReceived.Text = text
'To output to the label
temperature = text
Me.temp.Text = text
hmd.Text = temperature
End If
End Sub
If you want to overwrite the old value, you should not use &= but just = to assign the new value:
Me.dataReceived.Text = newText
&= is the same as Me.dataReceived.Text = Me.dataReceived.Text & newText
From MSDN:
The &= operator concatenates the String expression on its right to
the String variable or property on its left, and assigns the result to
the variable or property on its left.
Try this:
If (text IsNot Nothing) AndAlso (text.Trim().Length <> 0) Then
Me.dataReceived.Text = text
End If
I'm not that familiar with the SerialPort class, but I'll do my best to explain what's going on. The serial port raises the data received event when new data comes in through the serial port. Your code then reads all the existing data that has been received. When you call the ReadExisting method, it only returns what has been received up to that point. It does not wait for all the data to come in before it returns. It then calls the ReceivedText method which sets the value of some controls to the text that was received. The InvokeRequired and Invoke calls are only there to get you back on the UI thread. Apparently the SerialPort's DataReceived event may be raised on a separate thread, so you need to get back to the UI thread before you can do anything to any of the controls on the form. The change I suggested simply checked to make sure that the text recieved was not null or empty before you changed the value of the text box. The odd thing about the code is that it continues to read from the serial port until it is no longer open. I wouldn't think you'd want to do that. Rather, I would think that in each DataReceived event, you would just call ReadExisting once and then just expect it to be raised again the next time more data is received. By continuously calling ReadExisting in a loop, it must either return a null string or an empty string if there is no more data to read, which was why the text box was being blanked out.
&= is concatinating your text. use = to over write lable.
Me.dataReceived.Text = text
I'm not sure what is happening, but its likely that text does not have a value when setting the dataReceived.Text. Try this,
Private Sub ReceivedText(ByVal datatext As String)
Me.Invoke(Sub()
'To output to the text box:
Me.dataReceived.Text = datatext
Me.temp.Text = datatext
hmd.Text = datatext
End Sub)
End If
End Sub
I changed text to datatext since text could be confused with the forms local text property. I also moved the setting of the text properties of the labels/textboxes to an invoke using a lambda (note this syntax is new and only will work in VB 2010). I feel like somehow your invoke statement might not have been passing the string value properly. Also I removed the check for InvokeRequired, since the doing an invoke will work in both situations and it looks like you might just be doing threaded calls each time anyways.
If that compiles for you, it should work. If not, its likely that RecievedText is never being called. Set some breakpoints and step through the code to see that datatext has a value and that ReceivedText actually gets called.