TextBox Autocomplete with .NET Core 3.0

Load data in another thread and free the User Interface.

.NET Core 3.0 is the latest version of .NET Core and now supports WinForms and WPF. This sample shows how you can perform fill data to Autocomplete TextBox in another thread without slowing down the main UI (User Interface). With this sample, you can create a better user experience for the end-user.
 

TextBox Autocomplete

 
Autocomplete helps the user to search/type from a known list. In my application, I built a list to Autocomplete with > 10.000 items and is very fast even on slow computers.
 
Autocomplete is an old feature, but this post will demonstrate how to perform this to load data quickly in NET Core 3.0.
 
When you fill a Collection, if you have a huge suggestion list, this will make the UI (User Interface) freeze. But if you process it in another thread the UI will not have any issues.
  
More technical information is here.
 

How it works

 
 Like the other POST that uses a delegate, here, we use another thread to load data and delegate to bind the Autocomplete Collection in the current UI thread.
  1. using System;  
  2. using System.Data.SqlClient;  
  3. using System.Drawing;  
  4. using System.Windows.Forms;  
  5.   
  6. namespace TextBoxWithAutoComplete  
  7. {  
  8.   
  9.     /// <summary>  
  10.     /// 10-10-2019  
  11.     /// </summary>  
  12.     public partial class Form1 : Form  
  13.     {  
  14.   
  15.         private TextBox textBox1;  
  16.         public Form1()  
  17.         {  
  18.             InitializeComponent();  
  19.   
  20.             textBox1 = new TextBox  
  21.             {  
  22.                 Location = new Point(0, 0),  
  23.                 Size = new Size(100, 32),  
  24.                 Visible = true,  
  25.                 TabIndex = 0  
  26.             };  
  27.             Controls.Add(textBox1);  
  28.   
  29.             Load += Form1_Load;  
  30.         }  
  31.   
  32.         public void Form1_Load(object sender, EventArgs e)  
  33.         {  
  34.             Show(); // You need to show the form to avoid a thread error  
  35.   
  36.             LoadData(); // Start to load Data  
  37.         }  
  38.   
  39.         private SqlConnection GetConnection()  
  40.         {  
  41.             // Init your connection  
  42.             var oCnn = new SqlConnection  
  43.             {  
  44.                 ConnectionString = "YOUR_CONNECTION_STRING"  
  45.             };  
  46.             oCnn.Open();  
  47.             return oCnn;  
  48.         }  
  49.  
  50.  
  51.         #region TreadSafeLoading  
  52.         private delegate void UpdateUIDelegate(AutoCompleteStringCollection myCollection);  
  53.   
  54.         /// <summary>  
  55.         /// Load Data - You can make public to load as you need  
  56.         /// </summary>  
  57.         private void LoadData()  
  58.         => // Start this process in a new thread  
  59.             new System.Threading.Thread(() => OutOfThreadProcess()).Start();  
  60.         // You can start others, every one in a new thread!  
  61.   
  62.         /// <summary>  
  63.         /// Start fill in another Thread  
  64.         /// </summary>  
  65.         private void OutOfThreadProcess()  
  66.         {  
  67.             // inialize the collection  
  68.             var myCollection = new AutoCompleteStringCollection();  
  69.             using var oCnn = GetConnection();  
  70.   
  71.             // Start your SQL Query  
  72.             var cmd = new SqlCommand($"Select [FirstName] + ' ' + [LastName] as [contactName] From [AdventureWorks].[Person].[Contact] Order By [FirstName], [LastName]", oCnn);  
  73.   
  74.             // Read and fill the collection  
  75.             using var reader = cmd.ExecuteReader();  
  76.             while (reader.Read())  
  77.                 myCollection.Add(reader.GetString(0));  
  78.   
  79.             // Invoke in a UI thread  
  80.             _ = Invoke(new UpdateUIDelegate(UpdateUIInvoke), myCollection);  
  81.         }  
  82.   
  83.         /// <summary>  
  84.         /// Current UI Thread threatment  
  85.         /// </summary>  
  86.         /// <param name="myCollection"></param>  
  87.         private void UpdateUIInvoke(AutoCompleteStringCollection myCollection)  
  88.         {  
  89.             // Setup up in corrent UI Thread  
  90.             textBox1.AutoCompleteMode = AutoCompleteMode.SuggestAppend;  
  91.             textBox1.AutoCompleteSource = AutoCompleteSource.CustomSource;  
  92.             textBox1.AutoCompleteCustomSource = myCollection;  
  93. #if (IS_TELERIK_RadTextBoxControl)  
  94.             // If you use Telerik  
  95.             // I found an issue in Telerik by Progress, without fix.  
  96.             if (ParentForm != null)  
  97.                 ParentForm.FormClosing += new FormClosingEventHandler(UIFormClosing);  
  98. #endif  
  99.         }  
  100. #if (IS_TELERIK_RadTextBoxControl)  
  101.         private void UIFormClosing(object sender, FormClosingEventArgs e) => textBox1.AutoCompleteCustomSource = null;  
  102.  
  103. #endif  
  104.         #endregion  
  105.   
  106.     }  
  107. }  

Observation

 
When you use this technique you may receive errors if you run the new thread without firing the Show() method from the form, this occurs because the current window(form) hasn't initialized.
 

Conclusion

 
I designed this technique when I upgraded to .NET Core 3 and developed a fix for the FileSystemWatcher event that fires in a new thread, so I took the same idea to make Autocomplete load collection work in a new thread.
 
I hope you can bring a better experience to your end-users.
 
Happy coding.