Click here to Skip to main content
15,914,074 members
Please Sign up or sign in to vote.
4.33/5 (3 votes)
See more:
Hi All,

Using VB.Net in Visual Studio 2005.

Bit of a tricky one as far as I'm concernend this.

I'm trying to display a Form to the user that is split - the left side shows a multipage tif, separated onto a TabPage (tab pages = no. pages in tif).

On form starup I have a number of queries to perform to show corresponding data relating to the tif on the right of the screen.

The first attempt at doing all this wasn't using threads, and the form could take quite a while to display fully to the screen (sometimes 25 seconds).

What I am now trying to do is put the display of the tif and the queries into threads so that things can be done concurrently. The queries can take quite a while to come back (a second or two each in some cases).

A good idea I thought, the only problem being that whenver the code needs to update the form with some new data (i.e. a TextBox is populated) I need to call a function that does this on the main thread.

I then end up with multiple threads going on (not a problem) and each of those threads fighting to display data to the screen. The form can appear quicker, but with threads still happening underneath with form updates, it's really not ready for use until again, everything has finished.

Don't know if anyone has encountered this scenario before and could help with the best way forward?

Hope I've made myself clear enough and that someone has an idea.

Julian
Posted

If I understand you correctly you are running multiple queries to populate 1 screen.

If this is the case, organise your queries into one stored procedure on your server. This will reduce traffic as you only pass necessary parameters to the proc and receive the final dataset required, therefore speeding up your process.

Depending on the complexity and time for the proc to run you might need to use backgroung worker for the data while you display the tif.
 
Share this answer
 
Comments
julian@giant 17-Feb-11 9:58am    
Hi, thanks for responding so quickly. Good idea, except for the fact that we're using an Alpha database, and use a straight ADODB connection to perform SQL queries. Stored procedures aren't possible.
Thanks.
Julian
Hi,

You can call back to a main thread method using a call back delegate. Pass the methods pointer using a delegate to the thread. Call back using the delegate. For updating text controls in the main thread using method invoker (which again use a delegate).

As a example say I have 2 picture box and two text box. when I click a button i load the two picture box through 2 threads and callback the update text box methods as shown below. You can use background worker in the place of threads as well.

C#
public partial class Form1 : Form
{
    private  delegate void TextboxPopulate(string txt);

    public Form1()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        //Delegate of the method to call back from the thread
        TextboxPopulate del1 = new TextboxPopulate(populateTextBox1Invoker);
        //pass the delegate as a parameter
        ParameterizedThreadStart threadStart1 = new ParameterizedThreadStart(LoadPictureBox1);
        Thread thread1 = new Thread(threadStart1);
        thread1.Start(del1);


        TextboxPopulate del2 = new TextboxPopulate(populateTextBox2Invoker);
        ParameterizedThreadStart threadStart2 = new ParameterizedThreadStart(LoadPictureBox2);
        Thread thread2 = new Thread(threadStart2);
        thread2.Start(del2);
    }

    private void LoadPictureBox1(object textBoxPolpulateDelegate)
    {
        //do its work and call the callback method in the main thread
        pictureBox1.Image = new Bitmap("Flower1.jpg");
        object[] args=new object[1];
        args[0]="Rose";
        Delegate del1 = (Delegate)textBoxPolpulateDelegate;
        del1.DynamicInvoke(args);

        //This still works, but using a delegate is better option
        //populateTextBox1Invoker("Rose");
    }
    private void LoadPictureBox2(object textBoxPolpulateDelegate)
    {
        pictureBox2.Image = new Bitmap("Flower2.jpg");
        object[] args = new object[1];
        args[0] = "Tulip";
        Delegate del1 = (Delegate)textBoxPolpulateDelegate;
        del1.DynamicInvoke(args);

        //This still works, but using a delegate is better option
        //populateTextBox2Invoker("Tulip");

    }

    private void populateTextBox1Invoker(string txt)
    {
       //Handle the cross thread operation
        if (textBox1.InvokeRequired == true)
        {
            this.Invoke((MethodInvoker)delegate
            {
                textBox1.Text = txt;
            });

        }
        else
        {
            textBox1.Text = txt;
        }
    }

    private void populateTextBox2Invoker(string txt)
    {
        //Handle the cross thread operation
        if (textBox2.InvokeRequired == true)
        {

            this.Invoke((MethodInvoker)delegate
            {
                textBox2.Text =txt;
            });

        }
        else
        {
            textBox2.Text = txt;
        }
    }

}


Alternatively you can put all the queries in a async call, when the queries executing you can continue loading the images asynchronously. Once the images loaded you the queries also might be finished and ready to display.

An asynchronous example....

add a button to the above example and have delegate declaration at the top (global to form/ form instance variable)

C#
private delegate string QueryDelegate();


say this delegate for an example busy method and finally it returns a string.

the busy method...

C#
private string  myQueryProcess()
 {
     for (int i = 0; i < 5; i++)
     {
         Thread.Sleep(1000);
     }
     return "Query Completed";
 }



now in the button click event...
C#
private void button2_Click(object sender, EventArgs e)
{
    QueryDelegate del1 = new QueryDelegate(myQueryProcess);
    IAsyncResult result= del1.BeginInvoke(null,null);
    pictureBox1.Image = new Bitmap("Flower1.jpg");
    pictureBox2.Image = new Bitmap("Flower2.jpg");
    string resultStr=del1.EndInvoke(result);
    MessageBox.Show(resultStr);

}


you can see the images will be loaded without waiting for the busy method to be finished. the busy method will finish after 5 seconds.
 
Share this answer
 
v2
You could use a main procedure to load all the data initially, and run that as a thread, using lamda invokation to populate the data

C#
public partial class frmMain : Form
    {
        public frmMain()
        {
            InitializeComponent();
            PopulateData();
        }

        private bool frmLoaded
        {//disable or enable the form based on the data load
            get { return this.Enabled; }
            set { this.Enabled = value; }
        }

        private void PopulateData()
        {
            System.Threading.Thread T = new System.Threading.Thread(new System.Threading.ThreadStart(PopulateDateThread));
            T.IsBackground = true;
            T.Start();
            frmLoaded = false;
        }
        private void PopulateDateThread()
        {
            Image im1 = Database.GetImagefromDB();
            Image im2 = Database.GetImagefromDB();
            Image im3 = Database.GetImagefromDB();

            pictureBox1.Invoke(new Action<Image>((I) => pictureBox1.Image = I), new object[]{im1});
            pictureBox2.Invoke(new Action<Image>((I) => pictureBox2.Image = I), new object[]{im1});
            pictureBox3.Invoke(new Action<Image>((I) => pictureBox3.Image = I), new object[]{im1});
            this.Invoke(new Action<bool>((b) => this.frmLoaded = b), new object[] { true });
        }

    }
 
Share this answer
 
v2

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900