Making an auto typer from list view - vb.net

I currently have everything working as it should except for one thing, right now I have the user type into a textbox, press the Add button and it inserts the text into the list view, when the autotyper starts it starts a timer tick with 6000 intervals and types the list but does not go back to the beginning of the list it repeats the last known phrase.
EX:
How can I make it start back over from the beginning if there is nothing left to be typed?
My code for the timer tick
intervalTimer_Tick.Start()
Dim SeprateLine As String
Dim Separator As Integer
If ListedItems.Contains(vbLf) Then
Separator = ListedItems.IndexOf(vbLf)
SeprateLine = ListedItems.Remove(Separator)
ListedItems = ListedItems.Substring(Separator + 1)
Else
SeprateLine = ListedItems
End If
SendKeys.Send(SeprateLine)
SendKeys.Send("{ENTER}")
If ListedItems <> "" Then
End If
End Sub
My code for button
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
For Each LVI As ListViewItem In ListView1.Items
If ListedItems = "" Then
ListedItems = LVI.Text
Else
ListedItems &= vbLf & LVI.Text
End If
Next
intervalTimer_Tick.Start()
End Sub

I think it's great you've started coding in VB.NET. You are almost there, but I want to give you a suggestion. Don't store your values on your user interface - store them in code off the UI thread. Using plain old objects to keep data makes it easier to write and read. Consider a List(Of string) to hold the items, and using a ListBox and DataSource to present the data. This allows for definite separation between what is UI and what is data.
Private ReadOnly items As New List(Of String)()
Private ReadOnly timer As New System.Threading.Timer(AddressOf autoType, Nothing, -1, -1)
Private Sub AddButton_Click(sender As Object, e As EventArgs) Handles AddButton.Click
Dim s = InputBox("Add item", "Add", Nothing)
If s IsNot Nothing Then items.Add(s)
ListBox1.DataSource = Nothing
ListBox1.DataSource = items
End Sub
Private Sub RemoveButton_Click(sender As Object, e As EventArgs) Handles RemoveButton.Click
Dim si = ListBox1.SelectedIndex
If si >= 0 Then
items.RemoveAt(si)
ListBox1.DataSource = Nothing
ListBox1.DataSource = items
End If
End Sub
Private Sub StartButton_Click(sender As Object, e As EventArgs) Handles StartButton.Click
' different buttons / handlers for different intervals
timer.Change(5000, 6000) ' 5s delay before first tick, 6s between subsequent ticks
End Sub
Private Sub autoType(state As Object)
' this ticks off the UI thread, so must Control.Invoke to run on the UI
Me.Invoke(Sub() SendKeys.Send(String.Join(vbLf, items)))
End Sub
To stop it, just change the timer again
timer.Change(-1, -1)

Related

Loop through each item in Listbox and load a webpage

I'm trying to load a new webpage through the control webview2 in .net6+ windows forms, and I'm using a listbox to extract any single item and add it to the url to load on webview.
For example in listbox I have:
11
22
33
44
55
I would like at the press of a button that a loop starts to load one by one,each of these items like
WebView21.Source = New Uri("https://google.it" & ListBox1.Items.first & "rest of the url")
and after the webpage is loaded, it s supposed to extract it's html to check if a certain string is present with
Dim html As String
html = Await WebView21.ExecuteScriptAsync("document.documentElement.outerHTML;")
If html.Contains("Not found") Then
MsgBox("In Vacanza")
Else
MsgBox("Attivo")
End If
End Sub
after that, it goes back to the second listbox item, load the webview, check the html and so on.
My question is how can I loop the WebView in order to pick each of the items one by one and to proceed to do these little things in the while?
p.s. Once the loop arrives to the last listbox item, would it be possible to start it again from the first item?
Much thanks
edit1:
I'm trying with
Private ReadOnly resetEvent As New ManualResetEvent(False)
Async Sub scanWeb()
For Each listBoxElem As String In ListBox1.Items
resetEvent.Reset()
AddHandler WebView2.CoreWebView2.NavigationCompleted, AddressOf OnNavigationCompleted
WebView2.Source = New Uri("https://ikalogs.ru/tools/map/?page=1&server=22&world=10&state=active&search=city&allies%5B1%5D=&allies%5B2%5D=&allies%5B3%5D=&allies%5B4%5D=&nick=" & listBoxElem & "&ally=&island=&city=&x=&y=")
Await Task.Run(Sub() resetEvent.WaitOne())
RemoveHandler WebView2.CoreWebView2.NavigationCompleted, AddressOf OnNavigationCompleted
Dim html As String
html = Await WebView2.ExecuteScriptAsync("document.documentElement.outerHTML;")
If html.Contains("Not found") Then
DataGridView1.Rows.Add(listBoxElem, "IN vacanza")
Else
DataGridView1.Rows.Add(listBoxElem, "Attivo")
End If
Next
End Sub
Private Sub OnNavigationCompleted(sender As Object, e As CoreWebView2NavigationCompletedEventArgs)
resetEvent.Set()
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
WebView2 = New WebView2()
WebView2.EnsureCoreWebView2Async()
End Sub
but it seems the loop doesn't wait for the process to end and it goes straight to the next listbox item...
best way is to enumerate listBox items in a for-each loop : (I added an escape way -simple mouse clic on form - to quit loop)
Dim wStop As Boolean
sub scanWeb()
Do
For Each listBoxElem As String In ListBox1.Items
WebView21.Source = New Uri("https://google.it" & listBoxElem & "rest of the url")
'etc....
Next
Loop Until wStop = True
wStop = False
end sub
'way to stop scan
Private Sub form_clic(sender As Object, e As MouseEventArgs) Handles MyBase.MouseClick
wStop = True
End Sub
*** Update *****
The webview2 control is rather made to display data, and I have no experience on it.
Also I suggest you use a simpler method, based on System.Net.WebClient() and associated with threading.
Here is a start of code that works:
Dim wStop As Boolean
Dim i As Integer = 0
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Dim BackProcess = New Thread(Sub() Me.scanWeb())
BackProcess.Priority = ThreadPriority.Normal
BackProcess.Start()
end sub
Sub scanWeb()
Do While Not wStop
Dim WC As New System.Net.WebClient()
'change url for your need
Dim url As String = "https://www.google.fr/search?q=" & ListBox1.Items(i)
Dim s As System.IO.Stream = WC.OpenRead(url)
Dim sr As New System.IO.StreamReader(s)
Dim html As String = sr.ReadToEnd()
If html.Contains("Not found") Then 'beginInvoke allows the back task to communicate with UI
Me.BeginInvoke(Sub() DataGridView1.Rows.Add(ListBox1.Items(i), "In vacanza"))
Else
Me.BeginInvoke(Sub() DataGridView1.Rows.Add(ListBox1.Items(i), "Attivo"))
End If
i += 1
If i > ListBox1.Items.Count - 1 Then i = 0
Loop
End Sub
'button event to stop scanning
Private Sub stopScan_Click(sender As Object, e As EventArgs) Handles stopScan.Click
wStop = True
End Sub

How to show records added to datagridview in real time

A co-worker needs to search our network and her File Explorer search does not work well. I threw this app together quickly to allow her to search and it works well. The results are written to a datagridview, but the results are not shown until the search is complete.
I would like the datagridview to show records as they are added and allow her to cancel the search if she wants.
Using a backgroundworker, I tried to refresh the grid, but as soon as it finds a match, the code stops running. There are no errors, it just stops running.
So how can I get the grid to update as it continues to search?
Public dtResults As DataTable
Dim myDataSet As New DataSet
Dim myDataRow As DataRow
Dim colType As DataColumn
Dim colResult As DataColumn
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
dtResults = New DataTable()
colType = New DataColumn("Type", Type.GetType("System.String"))
colResult = New DataColumn("Search Result", Type.GetType("System.String"))
dtResults.Columns.Add(colType)
dtResults.Columns.Add(colResult)
DataGridView1.DataSource = dtResults
DataGridView1.Columns(1).AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill
End Sub
Private Sub btnSearch_Click(sender As Object, e As EventArgs) Handles btnSearch.Click
btnSearch.Enabled = False
sbStatusBar.Text = "Searching..."
dtResults.Clear()
BackgroundWorker1.RunWorkerAsync()
End Sub
Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
LoopSubFolders(txtSearchLocation.Text)
End Sub
Public Sub LoopSubFolders(sLocation As String)
Dim di = New DirectoryInfo(sLocation)
Dim mySearchterm As String = LCase(txtSearchTerm.Text)
Dim fiArr As FileInfo() = di.GetFiles()
Dim sSearchTarget As String
sbStatusBar.Text = "Searching " & sLocation
'Search File names in
If cbFileNames.Checked = True Then
For Each myFile In fiArr
sSearchTarget = LCase(myFile.Name)
If sSearchTarget.Contains(mySearchterm) Then
myDataRow = dtResults.NewRow()
myDataRow(dtResults.Columns(0)) = "File"
myDataRow(dtResults.Columns(1)) = Path.Combine(sLocation, myFile.Name)
dtResults.Rows.Add(myDataRow)
End If
Next
End If
For Each d In Directory.GetDirectories(sLocation)
If cbFolderNames.Checked = True Then
sSearchTarget = LCase(d)
If sSearchTarget.Contains(mySearchterm) Then
myDataRow = dtResults.NewRow()
myDataRow(dtResults.Columns(0)) = "Folder"
myDataRow(dtResults.Columns(1)) = d
dtResults.Rows.Add(myDataRow)
End If
End If
LoopSubFolders(d)
Next
End Sub
Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
btnSearch.Enabled = True
sbStatusBar.Text = "Complete"
DataGridView1.DataSource = Nothing
DataGridView1.DataSource = dtResults
DataGridView1.Columns(1).AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill
End Sub
Here's an example of how you might do it using the suggested ReportProgress method and ProgressChanged event:
Private table As New DataTable
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
'Configure table here.
DataGridView1.DataSource = table
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
'Setup UI here.
'Note that you MUST pass in the TextBox data as you MUST NOT touch the UI directly on the secondary thread.
BackgroundWorker1.RunWorkerAsync({TextBox1.Text, TextBox2.Text})
End Sub
Private Sub BackgroundWorker1_DoWork(sender As Object, e As DoWorkEventArgs) Handles BackgroundWorker1.DoWork
'Get the data passed in and separate it.
Dim arguments = DirectCast(e.Argument, String())
Dim folderPath = arguments(0)
Dim searchTerm = arguments(1)
SearchFileSystem(folderPath, searchTerm)
End Sub
Private Sub SearchFileSystem(folderPath As String, searchTerm As String)
For Each filePath In Directory.GetFiles(folderPath)
If filePath.IndexOf(searchTerm, StringComparison.InvariantCultureIgnoreCase) <> -1 Then
'Update the UI on the UI thread.
BackgroundWorker1.ReportProgress(0, {"File", filePath})
End If
Next
For Each subfolderPath In Directory.GetDirectories(folderPath)
If subfolderPath.IndexOf(searchTerm, StringComparison.InvariantCultureIgnoreCase) <> -1 Then
'Update the UI on the UI thread.
BackgroundWorker1.ReportProgress(0, {"Folder", subfolderPath})
End If
SearchFileSystem(subfolderPath, searchTerm)
Next
End Sub
Private Sub BackgroundWorker1_ProgressChanged(sender As Object, e As ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
'Get the data passed out and separate it.
Dim data = DirectCast(e.UserState, String())
'Update the UI.
table.Rows.Add(data)
End Sub
Note that you should NEVER touch the UI directly in the DoWork event handler or a method called from it. ONLY touch the UI on the UI thread. That means that the text in your TextBoxes must be extracted BEFORE calling RunWorkerAsync. You can eithewr pass the Strings in as arguments or you can assign them to fields and access them from there on any thread. Don't EVER access a member of a control on other than the UI thread. Some times it will work, sometimes it will appear to work but not do as intended and sometimes it will crash your app. So that you don't have to remember which specific scenarios cause which result, avoid such scenario altogether.
I haven't tested this code so I'm not sure but you may have to call Refresh on the grid or the form after adding the new row to the DataTable.
Variables
Well, let's start from the top with some class level variables:
'Notice the enabled properties.
Private WithEvents BackgroundWorker1 As New BackgroundWorker With {.WorkerReportsProgress = True, .WorkerSupportsCancellation = True}
'To monitor the cancellation, set by the Cancel Button.
Private bgwCancel As Boolean = False
'The DGV source.
Private dtResults As New DataTable
'The start directory.
Private startDir As String
'The search keyword.
Private searchWord As String
'Whether to search the sub directories, from a check box for example.
Private includeSubDirectories As Boolean = True
'Whether to search the files, from another check box.
Private includeFiles As Boolean = True
The Constructor
Prepare your DGV and whatever else you need here.
Sub New()
dtResults.Columns.Add(New DataColumn("Type", Type.GetType("System.String")))
dtResults.Columns.Add(New DataColumn("Search Result", Type.GetType("System.String")))
DataGridView1.DataSource = dtResults
DataGridView1.Columns(1).AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill
'Make sure you add the image column after binding the data source.
Dim imgCol As New DataGridViewImageColumn(False)
With imgCol
.Image = Nothing
.Name = "imgCol"
.HeaderText = ""
.Width = 50
.DefaultCellStyle.NullValue = Nothing
End With
DataGridView1.Columns.Insert(0, imgCol)
End Sub
Iterator
Now, let's write the search routine. I'd do that through an Iterator function:
Private Iterator Function IterateFolders(startDir As String, includeFiles As Boolean, includeSubDir As Boolean) As IEnumerable(Of String)
For Each dirName In IO.Directory.EnumerateDirectories(startDir)
Yield dirName
If includeFiles Then
For Each fileName In IO.Directory.EnumerateFiles(startDir)
Yield fileName
Next
End If
If includeSubDir Then
For Each subDir In IterateFolders(dirName, includeFiles, includeSubDir)
Yield subDir
Next
End If
Next
End Function
The Main Thread Updater
A routine called by the worker's thread to update the DataTable and any control that belongs to the main thread:
Private Sub AddSearchResult(path As String)
If InvokeRequired Then
Invoke(Sub() AddSearchResult(path))
Else
dtResults.Rows.Add(If(IO.File.Exists(path), "File", "Folder"), path)
sbStatusBar.Text = $"Searching {path}"
End If
End Sub
Start
In the click event of the start button, do the necessary validations, assign the values to their variables, and start the back ground worker:
If String.IsNullOrEmpty(txtSearchKeyword.Text) Then Return
If String.IsNullOrEmpty(txtSearchLocation.Text) Then Return
bgwCancel = False
dtResults.Rows.Clear()
startDir = txtSearchLocation.Text
searchWord = txtSearchKeyword.Text.ToLower
includeSubDirectories = chkIncludeSubDirs.Checked
includeFiles = chkFiles.Checked
btnSearch.Enabled = False
sbStatusBar.Text = "Searching..."
BackgroundWorker1.RunWorkerAsync()
Cancel
To cancel the search, in the click event of the cancel button I presume, True the bgwCancel variable:
bgwCancel = True
The BackgroundWorker - DoWork
Private Sub BackgroundWorker1_DoWork(sender As Object, e As DoWorkEventArgs) Handles BackgroundWorker1.DoWork
For Each item As String In IterateFolders(startDir, includeFiles, includeSubDirectories)
If bgwCancel Then
BackgroundWorker1.CancelAsync()
Return
End If
If item.ToLower.Contains(searchWord) Then
AddSearchResult(item)
End If
Threading.Thread.Sleep(100)
Next
End Sub
Note that, Its good practice to give a lengthy routine a BREATH through the Sleep(ms) method of that thread.
The BackgroundWorker - ProgressChanged
I don't think you need it here.
The BackgroundWorker - RunWorkerCompleted
Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
If bgwCancel Then
sbStatusBar.Text = "Canceled!"
MessageBox.Show("Canceled by you!")
ElseIf e.Error IsNot Nothing Then
sbStatusBar.Text = "Error!"
MessageBox.Show(e.Error.Message)
Else
sbStatusBar.Text = "Complete"
'YOU DO NOT NEED TO DO THIS. Remove the following
'DataGridView1.DataSource = Nothing
'DataGridView1.DataSource = dtResults
'DataGridView1.Columns(1).AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill
End If
btnSearch.Enabled = True
End Sub
The Image Column
Handle the RowsAdded event of the DGV as follow:
Private Sub DataGridView1_RowsAdded(sender As Object, e As DataGridViewRowsAddedEventArgs) Handles DataGridView1.RowsAdded
If DataGridView1.Columns.Count < 3 Then Return
'if you want to get rid of the default x image.
If e.RowIndex = 0 Then
DataGridView1.Rows(e.RowIndex).Cells("imgCol").Value = Nothing
End If
Dim path As String = DataGridView1.Rows(e.RowIndex).Cells(2).Value?.ToString
If Not String.IsNullOrEmpty(path) Then
If IO.File.Exists(path) Then
DataGridView1.Rows(e.RowIndex).Cells("imgCol").Value = Icon.ExtractAssociatedIcon(path).ToBitmap
Else
DataGridView1.Rows(e.RowIndex).Cells("imgCol").Value = My.Resources.Folder
End If
End If
End Sub
Where the My.Resources.Folder is an icon file of your choice for the folder entries.
Good luck.

Visual Basic Autotyper, Outputting listbox items

I'm trying to make a simple auto typer in Visual Basic. I want it to take the ListBox items and output at the user's choice of interval.
The problem is I don't know how to make the Timer's Tick event send each line and restart at the top of the list and continue to loop this way.
Form Design:
I have not listed much code because there really isn't much to list yet.
Private Sub intervalTimer_Tick(sender As Object, e As EventArgs) Handles intervalTimer.Tick
End Sub
Here is a code, but you need to replaced the ListBox with a ListView, it is the same look, this is just for the code to work.
This code is going to take each line from the ListView and output it using MsgBox using a timer that has an interval of user's choice, just remove the message box and output the result where ever you need, and you're all good :
First define this variable in your public class :
Dim ListedItems As String
Here is when you press the set button to set the interval :
Try
intervalTimer.Interval = intervalTextBox1.Text * 1000
Catch
MsgBox("The interval must be in purly seconds only!")
End Try
Here is when you press the start button :
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
For Each LVI As ListViewItem In ListView1.Items
If ListedItems = "" Then
ListedItems = LVI.Text
Else
ListedItems &= vbLf & LVI.Text
End If
Next
Timer1.Start()
End Sub
Here is when the timer tick event :
Private Sub intervalTimer_Tick(sender As Object, e As EventArgs) Handles intervalTimer.Tick
intervalTimer.Stop()
Dim SeprateLine As String
Dim Separator As Integer
If ListedItems.Contains(vbLf) Then
Separator = ListedItems.IndexOf(vbLf)
SeprateLine = ListedItems.Remove(Separator)
ListedItems = ListedItems.Substring(Separator + 1)
Else
SeprateLine = ListedItems
ListedItems = ""
End If
MsgBox(SeprateLine)
If ListedItems <> "" Then
intervalTimer.Start()
End If
End Sub
Finally i hope that this will help you with what you need :)

VB Thread Timer Update UI

I made a small program that allows for individual timer countdowns for each button clicked. (e.g. clicking on button 1 will start a countdown for button 1 whilst updating the text on the button itself to reflect the time remaining.)
My worry now is that I'm not sure how well my program would work in the long run. Here's a snippet of the code.
Private Sub depBtn_Clicked(sender As Button, e As EventArgs)
If sender.BackColor = Color.Green Then
Dim depRow() As Data.DataRow
Dim id As String = sender.Name
depRow = DepartmentDataSet.Departments.Select("ID Like '" & id & "'")
sender.BackColor = Color.Red
Dim timerBtn As New DepartmentTimer(sender, depRow(0)("Duration"), depRow(0)("ID"))
Dim TimerDelegate As New System.Threading.TimerCallback(AddressOf TimerTask)
Dim TimerItem As New System.Threading.Timer(TimerDelegate, timerBtn, 0, 1000)
timerBtn.timerRef = TimerItem
End If
End Sub
Private Delegate Sub TimerTaskDelegate(ByVal obj As Object)
Private Sub TimerTask(ByVal obj As Object)
If Me.InvokeRequired() Then
Me.Invoke(New TimerTaskDelegate(AddressOf TimerTask), obj)
Else
Dim depTimer As DepartmentTimer = DirectCast(obj, DepartmentTimer)
depTimer.countDown()
If depTimer.duration = -1 Then
depTimer.finish()
depTimer.timerRef.Dispose()
End If
End If
End Sub
I have read and also experienced that if I were to update on the UI thread directly from the timer callback the whole program would crash. So I ended up using a delegate in accordance to here http://tech.xster.net/tips/invoke-ui-changes-across-threads-on-vb-net/.
Is this a proper way of doing it or am I doing anything redundant/inefficient?
Also when I dispose of the Timer object. How would I go about cleaning up the DepartmentTimer class instance (timerBtn)? The button can be activated again once the timer runs out so I'm afraid that the instances would build up if I don't take care of them properly.
Thanks in advance for any help.
Since you're not actually doing anything with the Timer except immediately Invoking back to the main UI thread, you might as well just use one System.Windows.Forms.Timer and update them all in the same handler.
Something like:
Public Class Form1
Private timers As New List(Of DepartmentTimer)
Private WithEvents Tmr As New System.Windows.Forms.Timer
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
Tmr.Interval = 1000
Tmr.Start()
End Sub
Private Sub depBtn_Clicked(sender As Button, e As EventArgs)
If sender.BackColor = Color.Green Then
Dim depRow() As Data.DataRow
Dim id As String = sender.Name
depRow = DepartmentDataSet.Departments.Select("ID Like '" & id & "'")
sender.BackColor = Color.Red
timers.Add(New DepartmentTimer(sender, depRow(0)("Duration"), depRow(0)("ID")))
End If
End Sub
Private Sub Tmr_Tick(sender As Object, e As EventArgs) Handles Tmr.Tick
For i As Integer = timers.Count - 1 To 0 Step -1
Dim depTimer As DepartmentTimer = timers(i)
depTimer.countDown()
If depTimer.duration = -1 Then
depTimer.finish()
timers.RemoveAt(i)
End If
Next
End Sub
End Class

vb2008 search through listbox using textbox

hi I have a form that find, or should I say filter, the items in a listbox using a textbox. I have a textbox used for searching and a listbox populated with items from database. Now, say listbox items include apple, banana, berry, cashew, lemon, mango, peanut. If I typed 'b' on the textbox, listbox only show banana and berry..if I typed 'ba' then listbox only show banana but if I typed 'be' then it shows berry and so on. I already got this working (with the code marked as commented in the txtSearch event). My problem is that how can I bring the items in the listbox back when the user strike the backspace? Because, say I have banana and berry in the listbox now, when I erased the text that I typed in the textbox it should list back all the items again so that if I want to search another item it will be filtered again. thanks in advance.
Code Update
Public Class Glossary
Private Sub Glossary_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Call List()
Refreshlist()
End Sub
Private Sub List()
Dim myCmd As New MySqlCommand
Dim myReader As MySqlDataReader
Dim myAdptr As New MySqlDataAdapter
Dim myDataTable As New DataTable
Call Connect()
With Me
STRSQL = "Select word from glossary"
Try
myCmd.Connection = myConn
myCmd.CommandText = STRSQL
myReader = myCmd.ExecuteReader
If (myReader.Read()) Then
myReader.Close()
myAdptr.SelectCommand = myCmd
myAdptr.Fill(myDataTable)
lstword.DisplayMember = "word"
lstword.ValueMember = "word"
If myDataTable.Rows.Count > 0 Then
For i As Integer = 0 To myDataTable.Rows.Count - 1
lstword.Items.Add(myDataTable.Rows(i)("word"))
Next
End If
End If
'lstword.Items.Clear()
'lstword.Items.AddRange(word.Where(Function(word) word.ToString().Contains(txtSearch.Text)).ToArray())
Catch ex As Exception
MessageBox.Show(ex.Message)
End Try
myReader = Nothing
myCmd = Nothing
myConn.Close()
Call Disconnect()
End With
End Sub
Dim word As List(Of Object)
Private Sub Refreshlist()
lstword.Items.Clear()
lstword.Items.AddRange(word.Where(Function(word) word.ToString().Contains(txtSearch.Text)).ToArray())
End Sub
Private Sub txtSearch_TextChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles txtSearch.TextChanged
lstword.Items.Clear()
lstword.Items.AddRange(word.Where(Function(word) word.ToString().Contains(txtSearch.Text)).ToArray())
Refreshlist()
'Call List()
'lstword.BeginUpdate()
'Try
' ' keep track of the "non-searched items" '
' Dim word As New List(Of Object)
' lstword.SelectedIndices.Clear()
' If txtSearch.Text.Length > 0 Then
' For index As Integer = 0 To lstword.Items.Count - 1
' Dim item As String = lstword.Items(index).ToString()
' If item.IndexOf(txtSearch.Text, StringComparison.CurrentCultureIgnoreCase) >= 0 Then
' lstword.SelectedIndices.Add(index)
' Else
' ' this item was not searched for; we will remove it '
' word.Add(index)
' End If
' Next
' ' go backwards to avoid problems with indices being shifted '
' For i As Integer = word.Count - 1 To 0 Step -1
' Dim indexToRemove As Integer = word(i)
' lstword.Items.RemoveAt(indexToRemove)
' Next
' End If
'Finally
' lstword.EndUpdate()
'End Try
End Sub
End Class
The first step is to store the items in off-screen memory. For instance:
Dim words As List(Of Object)
Then when you refresh the list box, only populate it with the items from that in-memory list which match the current criteria:
lstword.Items.Clear()
lstword.Items.AddRange(
words.FindAll(
Function(word) Return word.ToString().Contains(txtSearch.Text)
).ToArray()
)
Or, using LINQ:
lstword.Items.Clear()
lstword.Items.AddRange(
words.Where(
Function(word) word.ToString().Contains(txtSearch.Text)
).ToArray()
)
UPDATE
Since you seem to be having trouble getting it working, and it's hard to say what's wrong with your code without actually seeing it, here's a complete working example:
Public Class Form1
Dim words As New List(Of Object)(New String() {"apple", "banana", "berry", "cashew", "lemon", "mango", "peanut"})
Private Sub RefreshList()
lstword.Items.Clear()
lstword.Items.AddRange(
words.Where(
Function(word) word.ToString().Contains(txtSearch.Text)
).ToArray()
)
End Sub
Private Sub txtSearch_TextChanged(sender As Object, e As EventArgs) Handles txtSearch.TextChanged
RefreshList()
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
RefreshList()
End Sub
End Class
UPDATE 2
I tried using your code with my recommended suggestions and it worked fine. Here's the code that worked for me. Try it and let me know if it doesn't work for you:
Public Class Glossary
Private Sub Glossary_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
List()
Refreshlist()
End Sub
Private Sub List()
word.AddRange(New String() {"apple", "banana", "berry", "cashew", "lemon", "mango", "peanut"})
End Sub
Private word As New List(Of Object)()
Private Sub Refreshlist()
lstword.Items.Clear()
lstword.Items.AddRange(word.Where(Function(word) word.ToString().Contains(txtSearch.Text)).ToArray())
End Sub
Private Sub txtSearch_TextChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles txtSearch.TextChanged
Refreshlist()
End Sub
End Class
If that works, then all you need to do is change the List method to load from the database instead of being a hard-coded list.
Get your initial list off the database, pop it in a List class e.g List.
The use your search text to select all the matching items and put them in the list box. Blank search text you just put all of them in.
You might want to have a look at Foreach and List.FindAll as well.