Prevent deleted to be search on searchbox - sql

I want to prevent the deleted Employee information to occur on my search bar as seen on the first picture there are only 3 users but when I put some numbers, this deleted one appear the code I use is as follows: Thankyouuu!
Private Sub TextBox1_TextChanged(sender As Object, e As EventArgs) Handles empId.TextChanged
Using db As New [Emme_Subic_Transport_Corporation_Payroll].EmmeSubicEntities
Dim drivers = db.UserDetails.Where(Function(c) c.EmployeeID.Contains(empId.Text)).ToList
If drivers.Count() > 0 Then
db.UserDetails.Where(Function(c) c.isDeleted <> 1).Load()
' db.UserDetails.Load()
UserDetailBindingSource.DataSource = db.UserDetails.Local
Else
MsgBox("No Match Found!")
End If
End Using
End Sub

Include another condition in the where which filters out the deleted users. And run the query off the UI thread. Here is a simple way to run your query on a background thread, off the UI, and it will only run when 1 second passes without an additional keystroke in the textbox
Private Sub TextBox1_TextChanged(sender As Object, e As EventArgs) Handles TextBox1.TextChanged
Static queryTimer As New System.Threading.Timer(AddressOf runQuery, Nothing, -1, -1)
queryTimer.Change(1000, -1)
End Sub
Private Sub runQuery(state As Object)
Dim employeeID As String
Me.Invoke(Sub() employeeID = empId.Text)
UserDetailBindingSource.DataSource = Nothing
Dim drivers As IEnumerable(Of UserDetails)
Using db As New [Emme_Subic_Transport_Corporation_Payroll].EmmeSubicEntities
drivers = db.UserDetails.Where(Function(c) c.EmployeeID.Contains(employeeID) AndAlso Not c.IsDeleted).ToList()
End Using
UserDetailBindingSource.DataSource = drivers
If Not drivers.Any() Then MsgBox("No Match Found!")
End Sub

Related

how to get the accessible folder names from a drive with vb.net (Visual Studio 2012 Express)

I am trying to create a program for a friend that allows him to "sort out" and locate his files.
He has a couple of drives and many folders. What I would like to do for him is:
Have code that lists all of the active drives in a combo box. He can then select which drive he wants to search for any or just selected file types by extension.
Then he either selects "Everything" or a specific type (ie: Music)
Once a button is clicked, a list box fills with the full path to the file
Beginning this writing process, I have accomplished this:
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Button1.Enabled = False
Dim allDrives() As DriveInfo = DriveInfo.GetDrives()
Dim d As DriveInfo
For Each d In allDrives
If d.IsReady Then ComboBox1.Items.Add(d)
Next
End Sub
Private Sub ComboBox1_SelectedIndexChanged(sender As Object, e As EventArgs) Handles ComboBox1.SelectedIndexChanged
If ComboBox1.SelectedIndex <> -1 Then
Try
For Each DName As String In My.Computer.FileSystem.GetDirectories(ComboBox1.Text, FileIO.SearchOption.SearchTopLevelOnly)
Dim LDN As Integer = Len(DName)
Dim FName As String = Strings.Mid(DName, 4, LDN - 3)
CheckedListBox1.Items.Add(FName, False)
Next
Catch ex As Exception
End Try
Else
Exit Sub
End If
End Sub
My first problem is that the GetDirectories process is getting directories I know that I do not have access to. I am trying to figure out how to prevent this. Can anyone help me.

Making an auto typer from list view

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)

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.

How to do a database search using a textbox and a search button in visual studio VB Code

I want to type something in my txtSearchCriteria textbox and when I hit the Perform Search button it displays all the records it finds into the other textboxes I have in my form. I am using a localdb file with a dataset in Visual Studio 2012. So far I have everything working except the search button. Here is my entire code in my project.
'Import Namespaces for SQL
Imports System.Data
Imports System.Data.SqlClient
Public Class Form1 Dim objCurrencyManager As CurrencyManager
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
'Loads data into the 'CustomersDataSet.Customers' table.
Me.CustomersTableAdapter.Fill(Me.CustomersDataSet.Customers)
'Controls Record Number Movement
objCurrencyManager = CType(Me.BindingContext(CustomersBindingSource), CurrencyManager)
'Display the current record number
Position()
'Display a tool tip at the bottom of screen
lblToolStripLabel1.Text = "Ready To Compute Data"
End Sub
Private Sub btnAdd_Click(sender As Object, e As EventArgs) Handles btnAdd.Click
'Used try catch block to validate all fields to ensure proper use of form and to display error if required fields are missing user imput
Try
Me.Validate()
Me.CustomersBindingSource.EndEdit()
Me.TableAdapterManager.UpdateAll(Me.CustomersDataSet)
lblToolStripLabel1.Text = "Record Saved" 'Display that record has been saved
Catch ex As Exception
MsgBox(ex.Message)
End Try
Position()
End Sub
Private Sub btnUpdate_Click(sender As Object, e As EventArgs) Handles btnUpdate.Click
'code to update an existing record and display that redord was updated successfully
Try
Me.Validate()
Me.CustomersBindingSource.EndEdit()
Me.TableAdapterManager.UpdateAll(Me.CustomersDataSet)
lblToolStripLabel1.Text = "Record Updated" 'display record updated
Catch ex As Exception
MsgBox(ex.Message)
End Try
Position()
End Sub
Private Sub dtnDelete_Click(sender As Object, e As EventArgs) Handles dtnDelete.Click
' Removes the current record and displays the new position in the database
Me.CustomersBindingSource.RemoveCurrent()
Position()
End Sub
Private Sub btnMoveFirst_Click(sender As Object, e As EventArgs) Handles btnMoveFirst.Click
'Moves the current record to the begining of the dataset. The First Record
Me.CustomersBindingSource.MoveFirst()
Position()
End Sub
Private Sub btnMovePrevious_Click(sender As Object, e As EventArgs) Handles btnMovePrevious.Click
'moves to the previous record and displays the position
Me.CustomersBindingSource.MovePrevious()
Position()
End Sub
Private Sub btnMoveNext_Click(sender As Object, e As EventArgs) Handles btnMoveNext.Click
'Moves to the next record in the dataset
Me.CustomersBindingSource.MoveNext()
Position()
End Sub
Private Sub btnMoveLast_Click(sender As Object, e As EventArgs) Handles btnMoveLast.Click
'Moves to the last Record in the Dataset
Me.CustomersBindingSource.MoveLast()
Position()
End Sub
Private Sub Position()
'This code displays the positio of the current record the user is viewing and shows it out of the number of recors that are held in the dataset
txtRecordPosition.Text = objCurrencyManager.Position + 1 & " Of " & objCurrencyManager.Count()
End Sub
Private Sub btnNew_Click(sender As Object, e As EventArgs) Handles btnNew.Click
'Clears the form for user imput, Created a new record and adds the current date to CustomerDate field and adds the next CustomerNumber available.
Me.CustomersBindingSource.AddNew()
txtCustomerNumber.Text = Me.CustomersBindingSource.Count + 1
txtCustomerDate.Text = Date.Today.ToShortDateString
End Sub
Private Sub btnPerformSort_Click(sender As Object, e As EventArgs) Handles btnPerformSort.Click
'Code to Sort by different field within the dataset
Select Case cboField.SelectedIndex
Case 0 'CustomerNumber
CustomersBindingSource.Sort = "CustomerNumber"
Case 1 'First Name
CustomersBindingSource.Sort = "FirstName"
Case 2 'Last Name
CustomersBindingSource.Sort = "LastName"
Case 3 'City
CustomersBindingSource.Sort = "City"
Case 4 'Province
CustomersBindingSource.Sort = "Province"
End Select
btnMoveFirst_Click(Nothing, Nothing)
lblToolStripLabel1.Text = "Records Sorted"
End Sub
Private Sub btnPerformSearch_Click(sender As Object, e As EventArgs) Handles btnPerformSearch.Click
'This will take the user imput from the txtSearchCriteria textbox and return all records that have the criteria
End Sub
End Class
Here is a sample to query database assuming you are connecting to sql server.
Private Sub BindGrid()
Dim constr As String = ConfigurationManager.ConnectionStrings("<your connection string name>").ConnectionString
Using con As New SqlConnection(constr)
Using cmd As New SqlCommand()
cmd.CommandText = "SELECT ContactName, City, Country FROM Customers WHERE ContactName LIKE '%' + #ContactName + '%'"
cmd.Connection = con
cmd.Parameters.AddWithValue("#ContactName", txtSearch.Text.Trim())
Dim dt As New DataTable()
Using sda As New SqlDataAdapter(cmd)
sda.Fill(dt)
gvCustomers.DataSource = dt
gvCustomers.DataBind()
End Using
End Using
End Using
End Sub
To explain at first step we are declaring connection string then we are opening SqlConnection object and then we are querying the database table ContactName based on the txtSearch value inputted.Next we are filling the dataset and binding the grid.
I suggest you to read this msdn article on sql connection.

.NET fulltext autocomplete in a combobox. Any performance-positive way of overriding listitems?

I'm struggling to meet a demand from my supervisors. I really hope that someone could give some advice.
Basically there are places in our project where there is a big selection of options. The most concrete example is choosing a city in the world. The items are hundreds of thousands.
Using standard winforms controls and properties, one can search through a list fast.
The problem is that we're using a concatenation of city&district name for all the items. Essentially PREFIX autotomplete works but does not work as needed. The task is to filter and show items by any given string in any part of the item. Essentially a FULL TEXT search in the combobox.
Does anyone have an idea about switching autocomplete sources in runtime relatively qiuckly and handling the suggest/suggestappend event?
Also the project is in VB.NET, though any form of .NET advice will be extremely helpful.
Thanks!
UPDATE: The latest attempt using competent_tech's suggestion with some minor modifications.
Imports System.Data
Public Class Form1
Private _ErrorText As String
Private _CommandExecuted As Boolean
Private m_fOkToUpdateAutoComplete As Boolean
Private m_sLastSearchedFor As String = ""
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Call Me.SetStatusText("Loading...")
Me._ErrorText = ""
Me.Cities.Clear()
Me.BackgroundWorker1.RunWorkerAsync()
End Sub
Private Sub BackgroundWorker1_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
Try
Me._CommandExecuted = True
Me.Ara_airportsTableAdapter.Fill(Me.Cities.ara_airports)
Catch ex As Exception
_ErrorText = ex.Message
End Try
End Sub
Private Sub BackgroundWorker1_RunWorkerCompleted(ByVal sender As Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
If Me._ErrorText = "" Then
Me.SetStatusText(Me.Cities.ara_airports.Count & " Records loaded")
Else
Me.SetStatusText(Me._ErrorText)
End If
Me.BindingSource.ResetBindings(False)
End Sub
Private Sub SetStatusText(ByVal sText As String)
Me.Text = sText
End Sub
Private Sub cboPort_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles cboPort.KeyDown
Try
If e.KeyCode = Keys.Up OrElse e.KeyCode = Keys.Down Then
m_fOkToUpdateAutoComplete = False
Else
m_fOkToUpdateAutoComplete = True
End If
Catch theException As Exception
' ...
End Try
End Sub
Private Sub cboPort_TextChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles cboPort.KeyUp
Try
If m_fOkToUpdateAutoComplete Then
With cboPort
If .Text.Length >= 2 Then
Dim cSuggestions As IList
Dim sError As String = ""
m_sLastSearchedFor = .Text
cSuggestions = GetSuggestions(m_sLastSearchedFor)
.DataSource = Nothing
If cSuggestions IsNot Nothing Then
.BindingContext = New BindingContext
.DisplayMember = "CName"
.ValueMember = "id"
.DataSource = New BindingSource(cSuggestions, Nothing)
System.Threading.Thread.Sleep(10)
System.Windows.Forms.Application.DoEvents()
.DroppedDown = True
.Text = m_sLastSearchedFor
If .Text.Length > 0 Then .SelectionStart = .Text.Length
End If
End If
End With
End If
Catch theException As Exception
' ...
End Try
End Sub
Private Function GetSuggestions(ByVal searchFor As String) As IList
BindingSource.Filter = "CName LIKE '%" & searchFor & "%'"
Return BindingSource.List
End Function
End Class
The way we address this with very large sets of data (full set of drug information) is:
1) Handle the combo's TextChanged event
2) Within this event, get the list of suggestions that match the user's current input from the database. We leverage the power of database searching to find matches anywhere within the string.
3) When the suggestions are retrieved, bind them to the combobox
4) Wait for a little bit (500ms) to let the UI catch up (we use a combination of System.Threading.Thread.Sleep and System.Windows.Format.Application.DoEvents()).
A couple of notes on this approach:
1) Nothing is bound to the list when the form is first opened
2) We wait until the user has entered at least 4 characters before we start searching to reduce the hit on the DB and improve the user experience (you don't want to show all of the matches for A, for example).
Update with code to show full solution:
Here are some additional notes and code to show the actual process.
The ComboBox should be configured with all of the properties set to their default values with the exception of:
AutoCompleteMode = SuggestAppend
PreferredDropDownSize = 0, 0
Here is the code that we use for our specific situation (searching first four chars) with a placeholder for retrieving and assigning the data:
Private m_fOkToUpdateAutoComplete As Boolean
Private m_sLastSearchedFor As String = ""
Private Sub cboName_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles cboName.KeyDown
Try
' Catch up and down arrows, and don't change text box if these keys are pressed.
If e.KeyCode = Keys.Up OrElse e.KeyCode = Keys.Down Then
m_fOkToUpdateAutoComplete = False
Else
m_fOkToUpdateAutoComplete = True
End If
Catch theException As Exception
' Do something with the error
End Try
End Sub
Private Sub cboName_TextChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles cboName.TextChanged
Try
If m_fOkToUpdateAutoComplete Then
With cboName
If .Text.Length >= 4 Then
' Only do a search when the first 4 characters have changed
If Not .Text.Substring(0, 4).Equals(m_sLastSearchedFor, StringComparison.InvariantCultureIgnoreCase) Then
Dim cSuggestions As IEnumerable
Dim sError As String = ""
' Record the last 4 characters we searched for
m_sLastSearchedFor = .Text.Substring(0, 4)
' And search for those
cSuggestions = GetSomeSuggestions(m_sLastSearchedFor) ' Your code here
.DataSource = Nothing
If cSuggestions IsNot Nothing Then
' Because this can use the same data source as the list, ensure that
' the bindingcontexts are different so that the lists are not tied to each other
.BindingContext = New BindingContext
.DataSource = cSuggestions
' Let the UI process the results
System.Threading.Thread.Sleep(10)
System.Windows.Forms.Application.DoEvents()
End If
End If
Else
If Not String.IsNullOrEmpty(m_sLastSearchedFor) Then
' Clear the last searched for text
m_sLastSearchedFor = ""
cboName.DataSource = Nothing
End If
End If
End With
End If
Catch theException As Exception
' Do something with the error
End Try
End Sub