October 11, 2019 11:56 by
Peter
.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 slow down the main UI (User Interface). With this sample, you can do a better user experience for the end-user.
TextBox Autocomplete
Autocomplete helps the user to search/type from a know list. In my application, I build 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 his use to load data fastly already in NET Core 3.0.
When you fill a Collection, if you have a huge suggestion list, this will make the UI (User Interface) freezes. But if you process in another thread the UI will not have any issues.
More technical information you can have here.
How it works
Like the other POST what uses a delegate, here, we use another thread to load data and delegate to bind the Autocomplete Collection in the current UI thread.
using System;
using System.Data.SqlClient;
using System.Drawing;
using System.Windows.Forms;
namespace TextBoxWithAutoComplete
{
/// <summary>
/// 10-10-2019
/// </summary>
public partial class Form1 : Form
{
private TextBox textBox1;
public Form1()
{
InitializeComponent();
textBox1 = new TextBox
{
Location = new Point(0, 0),
Size = new Size(100, 32),
Visible = true,
TabIndex = 0
};
Controls.Add(textBox1);
Load += Form1_Load;
}
public void Form1_Load(object sender, EventArgs e)
{
Show(); // You need to show the form to avoid a thread error
LoadData(); // Start to load Data
}
private SqlConnection GetConnection()
{
// Init your connection
var oCnn = new SqlConnection
{
ConnectionString = "YOUR_CONNECTION_STRING"
};
oCnn.Open();
return oCnn;
}
#region TreadSafeLoading
private delegate void UpdateUIDelegate(AutoCompleteStringCollection myCollection);
/// <summary>
/// Load Data - You can make public to load as you need
/// </summary>
private void LoadData()
=> // Start this process in a new thread
new System.Threading.Thread(() => OutOfThreadProcess()).Start();
// You can start others, every one in a new thread!
/// <summary>
/// Start fill in another Thread
/// </summary>
private void OutOfThreadProcess()
{
// inialize the collection
var myCollection = new AutoCompleteStringCollection();
using var oCnn = GetConnection();
// Start your SQL Query
var cmd = new SqlCommand($"Select [FirstName] + ' ' + [LastName] as [contactName] From [AdventureWorks].[Person].[Contact] Order By [FirstName], [LastName]", oCnn);
// Read and fill the collection
using var reader = cmd.ExecuteReader();
while (reader.Read())
myCollection.Add(reader.GetString(0));
// Invoke in a UI thread
_ = Invoke(new UpdateUIDelegate(UpdateUIInvoke), myCollection);
}
/// <summary>
/// Current UI Thread threatment
/// </summary>
/// <param name="myCollection"></param>
private void UpdateUIInvoke(AutoCompleteStringCollection myCollection)
{
// Setup up in corrent UI Thread
textBox1.AutoCompleteMode = AutoCompleteMode.SuggestAppend;
textBox1.AutoCompleteSource = AutoCompleteSource.CustomSource;
textBox1.AutoCompleteCustomSource = myCollection;
#if (IS_TELERIK_RadTextBoxControl)
// If you use Telerik
// I found an issue in Telerik by Progress, without fix.
if (ParentForm != null)
ParentForm.FormClosing += new FormClosingEventHandler(UIFormClosing);
#endif
}
#if (IS_TELERIK_RadTextBoxControl)
private void UIFormClosing(object sender, FormClosingEventArgs e) => textBox1.AutoCompleteCustomSource = null;
#endif
#endregion
}
}
Observation
When you use this technic may you receive errors if you run the new thread without fire the Show() method from the form, this occurs because the current window(form) hasn't the Handle initialized.
Conclusion
I design this technic when I upgrade to .NET Core 3 and develop a fix for the FileSystemWatcher event that fires in a new thread, so I took the same idea to make Autocomplete load collection works in a new thread.