Blog Archives
C# Application Not Responding Issue on Long Running Threads
Undoubtedly many of you have encountered the dreaded “Application Not Responding” message, programmers and non-programmers alike.
In general, this is caused by the OS misinterpretation of long delays in form refresh, even though these can easily be caused by a long running process, or any running process longer than a couple seconds actually.
If this is happening in your application after clicking on a button or event fires, a quick solution would be the easy one-liner below:
Application.DoEvents();
However, to really understand why this happening, remember that all processes (may) contain multiple threads.
In the case of your typical quick shot solution that you put together in VS in no-time, you’re likely only using a single thread.
This thread is used not only to run the processes in your application, but also to update the form window including drawing the fields and various controls on the form.
The best solution to this would really be to create a separate thread and pass off the work of your long running function to this thread.
When the work is finished, it will update our main thread (the form thread) that it has completed which we can intercept with a delegate, and notify the user of success or failure.
See snippet below.
private BackgroundWorker hardworker; //this guys works hard! private System.Timers.Timer lazyworker; //notice how this could also be achieved with thread/timer protected void btnDoAlotOfWork_Click() { //lets see who accomplishes more! ClockInLazyWorker(); ClockInHardWorker(); } private void ClockInLazyWorker() { lazyworker = new System.Timers.Timer(); //not to be confused with forms timer lazyworker.Interval = 1000; //time in milliseconds. 10 seconds is lazy for a computer! lazyworker.Elapsed += new System.Timers.ElapsedEventHandler(LetsGetAMoveOnIt()); } private void ClockInHardWorker() { hardworker= new BackgroundWorker(); mCopier.DoWork += DoWork; //all the heavy lifting is done here mCopier.RunWorkerCompleted += WorkCompleted(); mCopier.WorkerSupportsCancellation = true; OnError += ProblemWithWork(); } //motivational function name made especially for our lazy worker private void LetsGetAMoveOnIt() { //do something that takes a long time //if the data you operate on is the same as used by another thread or the form thread, make sure you wrap this block in a synclock or use a bool to track when work is in process } //created separate function for the background worker, and also to avoid synclock issues since there are two new active threads private void DoWork() { //if the data you operate on is the same as used by another thread or the form thread, make sure you wrap this block in a synclock or use a bool to track when work is in process. same goes for timer. } private void WorkCompleted() { //all finished } private void ProblemWithWork() { //hopefully not an injury on the job! that's workmans comp! }
Alot more code than simply “Application.DoEvents”.. but do not underestimate the power of multiple threads!
URL Decode in WPF
Crossing from web to forms development you may notice System.Web is not available. You could extract it from the GAC, but would suffer from having to manually update it moving forward.
One solution on the web suggested using Microsoft.XSS library (which is up to version 4.0 at the time of this article).
This would work, but there are some differences in string conversion, especially regarding the “+” and “~” character between using the Web UrlEncode/UrlDecode found in the XSS library or using the Uri method illustrated below.
See references for links to XSS and/or information regarding the differences on how these strings are encoded differently with each method.
Uri videouri = new Uri(AppDomain.CurrentDomain.BaseDirectory + "../../Videos/directory/player.htm"); string videourl = Uri.UnescapeDataString(videouri.ToString()); webBrowser1.Navigate(videourl); //can actually accept uri or string
References
Microsoft Anti-XSS Library 4.0, http://www.microsoft.com/downloads/en/details.aspx?FamilyID=F4CD231B-7E06-445B-BEC7-343E5884E651
Nerdbank, http://blog.nerdbank.net/2009/05/uriescapedatapath-and.html
StackOverflow, http://stackoverflow.com/questions/36315/alternative-to-httputility-for-net-3-5-sp1-client-framework
Winforms Databinding
Snippets below have been condensed from their original sources for brevity. See references for original articles.
Dataset usage:
using System; using System.Data; using System.Data.SqlClient; namespace Microsoft.AdoNet.DataSetDemo { class NorthwindDataSet { static void Main() { string connectionString = GetConnectionString(); ConnectToData(connectionString); } private static void ConnectToData(string connectionString) { //Create a SqlConnection to the Northwind database. using (SqlConnection connection = new SqlConnection(connectionString)) { //Create a SqlDataAdapter for the Suppliers table. SqlDataAdapter adapter = new SqlDataAdapter(); // A table mapping names the DataTable. adapter.TableMappings.Add("Table", "Suppliers"); // Open the connection. connection.Open(); Console.WriteLine("The SqlConnection is open."); // Create a SqlCommand to retrieve Suppliers data. SqlCommand command = new SqlCommand( "SELECT SupplierID, CompanyName FROM dbo.Suppliers;", connection); command.CommandType = CommandType.Text; // Set the SqlDataAdapter's SelectCommand. adapter.SelectCommand = command; // Fill the DataSet. DataSet dataSet = new DataSet("Suppliers"); adapter.Fill(dataSet); // Create a second Adapter and Command to get // the Products table, a child table of Suppliers. SqlDataAdapter productsAdapter = new SqlDataAdapter(); productsAdapter.TableMappings.Add("Table", "Products"); SqlCommand productsCommand = new SqlCommand( "SELECT ProductID, SupplierID FROM dbo.Products;", connection); productsAdapter.SelectCommand = productsCommand; // Fill the DataSet. productsAdapter.Fill(dataSet); // Close the connection. connection.Close(); Console.WriteLine("The SqlConnection is closed."); // Create a DataRelation to link the two tables // based on the SupplierID. DataColumn parentColumn = dataSet.Tables["Suppliers"].Columns["SupplierID"]; DataColumn childColumn = dataSet.Tables["Products"].Columns["SupplierID"]; DataRelation relation = new System.Data.DataRelation("SuppliersProducts", parentColumn, childColumn); dataSet.Relations.Add(relation); Console.WriteLine( "The {0} DataRelation has been created.", relation.RelationName); } } static private string GetConnectionString() { // To avoid storing the connection string in your code, // you can retrieve it from a configuration file. return "Data Source=(local);Initial Catalog=Northwind;" + "Integrated Security=SSPI"; } } }
Binding DataGridView:
private void GetData(string selectCommand) { try { // Specify a connection string. Replace the given value with a // valid connection string for a Northwind SQL Server sample // database accessible to your system. String connectionString = "Integrated Security=SSPI;Persist Security Info=False;" + "Initial Catalog=Northwind;Data Source=localhost"; // Create a new data adapter based on the specified query. dataAdapter = new SqlDataAdapter(selectCommand, connectionString); // Create a command builder to generate SQL update, insert, and // delete commands based on selectCommand. These are used to // update the database. SqlCommandBuilder commandBuilder = new SqlCommandBuilder(dataAdapter); // Populate a new data table and bind it to the BindingSource. DataTable table = new DataTable(); table.Locale = System.Globalization.CultureInfo.InvariantCulture; dataAdapter.Fill(table); bindingSource1.DataSource = table; // Resize the DataGridView columns to fit the newly loaded content. dataGridView1.AutoResizeColumns( DataGridViewAutoSizeColumnsMode.AllCellsExceptHeader); } catch (SqlException) { MessageBox.Show("To run this example, replace the value of the " + "connectionString variable with a connection string that is " + "valid for your system."); } }
References:
MSDN, “DataSet Class”, http://msdn.microsoft.com/en-us/library/system.data.dataset.aspx
MSDN, “How to: Bind Data to the Windows Forms DataGridView Control”, http://msdn.microsoft.com/en-us/library/fbk67b6z.aspx