views:

155

answers:

4

I'm doing an application that does some sort of scanning (it checks availability of URL's through a short list) and depending on the result, it adds to one or another listbox. if it exists, it goes to lstOK, else, it goes to lst404.

The issue is that these web checks take time (specially when it is OK), it takes an awfully long time, and inserts all the items in the listboxes in the end, while the form is "not responding" and nothing appears or can be clicked or displays any interaction.

Is there a way for the form to be still usable and the listboxes to update on the go ?

This should be simple, I just don't know it (yet)

I'm using C# in Visual Studio

--[update]--
The whole url checking is in one single function Start()

+4  A: 

try the background worker

Sam Holder
+1  A: 

If it's a web form look into AJAX.NET. There are several controls (UpdatePanel being one off the top of my head) that will help you do this.

Take a look at the toolkit.

EDIT: Only for web apps.

Tim
+1  A: 

Application.DoEvents(); will do all the events that have happened up to that point. so in your loop, after each website is checked, for example. do Application.DoEvents(); on the other hand if you just want to refresh your listboxes it'll be listboxname.Refresh();
both of these options, however will still have a time where it freezes while the website is pinged, unless you do many of them, which i dont suggets doing.
both methods also only use a single thread and is very linear.
The best option would be to create a new thread to do the tests on, or use a background worker that can do the tests on a seperate thread, so the events of the form can be handled instantly without a need to wait.

Manually controlling another thread shouldnt be too difficult. here's an example.

using System.Threading;

public class MultiThreadingClass
{
    private void FunctionForNewThread()
    {
    //do stuff
    }

    private void FunctionWithParameter(object param)
    {
    //Should do checks with typeof() on param before casting
    int convertedparam = (int)param;
    //do stuff
    }
    Thread t, t2;
    static void Main()
    {
        ThreadStart ts = new ThreadStart(FunctionForNewThread);
        t = new Thread(ts);
        t.Start();
        int x = 5;
        ParameterizedThreadStart pts = new ParameterizedThreadStart(FunctionWithParameter);
        t2 = new Thread(pts);
        t2.Start(x);
    }
}

it may be important to note here that you should never add a Thread as a local variable that will dissapear, as you can only really get the thread instance back by doing Thread.CurrentThread in the function which was called by the new thread, but if that thread has already locked up, you have a bit of a problem there :)

To easily handle Threads in a global variable either create an Array of threads and call Thread.Abort(); on each running thread when the program closes, or use the ThreadPool class in System.Threading.

Skintkingle
down this path many problems lie IMHO.
Sam Holder
Application.DoEvents() should be avoided. Use the background worker, that's what it's designed for.
Paolo
i did cover many aspects including the background worker and multi-threading.i agree Application.DoEvents() is a very bad option, i was simply covering all the options. That is why i said in my answer the best option is another thread or the background worker.Please dont needlessly downvote.
Skintkingle
upvoted you back. I'm currently without time to do this (i didn't think creating another thread would be so extense) so i've put this instead for me to use this personal app until i get the time to make the final solution. Thank you. note: don't EVER downvote someone because it is not the best answer, only downvote when it's wrong and is going to make the asker lose time. Thank you, skintkingle.
MarceloRamires
I've edited my answer with an example of using threads manually, I hope it will shed some light on your Thread creating problem.
Skintkingle
+2  A: 

If this is a desktop application that is performing these "web checks" then you can use a BackgroundWorkerThread to perform the processing, and get the results.

Or you could do something like this:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;

namespace ThreadWithDataReturnExample
{
    public partial class Form1 : Form
    {
        private Thread thread1 = null;

        public Form1()
        {
            InitializeComponent();

            thread1 = new Thread(new ThreadStart(this.threadEntryPoint));
            Thread1Completed += new AsyncCompletedEventHandler(thread1_Thread1Completed);
        }

        private void startButton_Click(object sender, EventArgs e)
        {
            thread1.Start();
            //Alternatively, you could pass some object
            //in such as Start(someObject);
            //With apprioriate locking, or protocol where
            //no other threads access the object until
            //an event signals when the thread is complete,
            //any other class with a reference to the object 
            //would be able to access that data.
            //But instead, I'm going to use AsyncCompletedEventArgs 
            //in an event that signals completion
        }

        void thread1_Thread1Completed(object sender, AsyncCompletedEventArgs e)
        {
            if (this.InvokeRequired)
            {//marshal the call if we are not on the GUI thread                
                BeginInvoke(new AsyncCompletedEventHandler(thread1_Thread1Completed),
                  new object[] { sender, e });
            }
            else
            {
                //display error if error occurred
                //if no error occurred, process data
                if (e.Error == null)
                {//then success

                    MessageBox.Show("Worker thread completed successfully");
                    DataYouWantToReturn someData = e.UserState as DataYouWantToReturn;
                    MessageBox.Show("Your data my lord: " + someData.someProperty);

                }
                else//error
                {
                    MessageBox.Show("The following error occurred:" + Environment.NewLine + e.Error.ToString());
                }
            }
        }

        #region I would actually move all of this into it's own class
            private void threadEntryPoint()
            {
                //do a bunch of stuff

                //when you are done:
                //initialize object with data that you want to return
                DataYouWantToReturn dataYouWantToReturn = new DataYouWantToReturn();
                dataYouWantToReturn.someProperty = "more data";

                //signal completion by firing an event
                OnThread1Completed(new AsyncCompletedEventArgs(null, false, dataYouWantToReturn));
            }

            /// <summary>
            /// Occurs when processing has finished or an error occurred.
            /// </summary>
            public event AsyncCompletedEventHandler Thread1Completed;
            protected virtual void OnThread1Completed(AsyncCompletedEventArgs e)
            {
                //copy locally
                AsyncCompletedEventHandler handler = Thread1Completed;
                if (handler != null)
                {
                    handler(this, e);
                }
            }
        #endregion

    }
}
AaronLS