2

Possible Duplicate:
How to update GUI from another thread in C#?

I've currently got a C# program to run a query and display the results in a datagridview.

The query due to size of records takes a while (20-30 seconds) to run.

I thought I would add an animation so the user at least knows the software is running and has not stopped working.

Of course I can't run anything when the call is being made to the procedure so I looked into threading.

Here is my code (forgive me, I haven't really put in comments yet):

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

namespace RepSalesNetAnalysis
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            pictureBox2.Visible = false;
        }

        private void button1_Click(object sender, EventArgs e)
        {
            GetsalesFigures();   
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            AutofillAccounts();
        }


        private void GetsalesFigures()
        {
            try
            {
                string myConn = "Server=herp;" +
                            "Database=shaftdata;" +
                            "uid=fake;" +
                            "pwd=faker;" +
                            "Connect Timeout=120;";

                string acct;// test using 1560
                SqlConnection conn = new SqlConnection(myConn);
                SqlCommand Pareto = new SqlCommand();
                BindingSource bindme = new BindingSource();
                SqlDataAdapter adapt1 = new SqlDataAdapter(Pareto);
                DataSet dataSet1 = new DataSet();
                DataTable table1 = new DataTable();

                Thread aniSql = new Thread(new ThreadStart(animateIcon));//CREATE THE THREAD


                acct = accCollection.Text;

                string fromDate = this.dateTimePicker1.Value.ToString("MM/dd/yyyy");
                string tooDate = this.dateTimePicker2.Value.ToString("MM/dd/yyyy");

                Pareto.Connection = conn;
                Pareto.CommandType = CommandType.StoredProcedure;
                Pareto.CommandText = "dbo.GetSalesParetotemp";
                Pareto.CommandTimeout = 120;

                Pareto.Parameters.AddWithValue("@acct", acct);
                Pareto.Parameters.AddWithValue("@from", fromDate);
                Pareto.Parameters.AddWithValue("@too", tooDate);

                aniSql.Start();                //START THE THREAD!
                adapt1.Fill(dataSet1, "Pareto");
                aniSql.Abort();                //KILL THE THREAD!
                //pictureBox2.Visible = false;

                this.dataGridView1.AutoGenerateColumns = true;
                this.dataGridView1.DataSource = dataSet1;
                this.dataGridView1.DataMember = "Pareto";

                dataGridView1.AutoResizeColumns(
                    DataGridViewAutoSizeColumnsMode.AllCells);

            }
            catch (Exception execc)
            {
                MessageBox.Show("Whoops! Seems we couldnt connect to the server!"
                            + " information:\n\n" + execc.Message + execc.StackTrace,
                            "Fatal Error", MessageBoxButtons.OK, MessageBoxIcon.Stop);
                }

        }

        private void AutofillAccounts()
        {
            //get customers list and fill combo box on form load.
            try
            {
                string myConn1 = "Server=derp;" +
                                "Database=AutoPart;" +
                                "uid=fake;" +
                                "pwd=faker;" +
                                "Connect Timeout=6000;";
                SqlConnection conn1 = new SqlConnection(myConn1);
                conn1.Open();
                SqlCommand accountFill = new SqlCommand("SELECT keycode FROM dbo.Customer", conn1);

                SqlDataReader readacc = accountFill.ExecuteReader();

                while (readacc.Read())
                {
                    this.accCollection.Items.Add(readacc.GetString(0).ToString());
                }
                conn1.Close();
            }
            catch(Exception exc1)
            {
                MessageBox.Show("Whoops! Seems we couldnt connect to the server!"
                            + " information:\n\n" + exc1.Message + exc1.StackTrace,
                            "Fatal Error", MessageBoxButtons.OK, MessageBoxIcon.Stop);
            }
        }
        public void animateIcon()
        {
            // animate
            pictureBox2.Visible = true;  
        }
    }
}

As you can see I want to run the animation just before the procedure call and then end it just after.

My knowledge on threads is brand new. I've looked around but i'm getting a little confused at the moment.

Here's my error:

Thrown: "Cross-thread operation not valid: Control 'Form1' accessed from a thread other than the thread it was created on." (System.InvalidOperationException) Exception Message = "Cross-thread operation not valid: Control 'Form1' accessed from a thread other than the thread it was created on.", Exception Type = "System.InvalidOperationException"

I need a very simple way of performing an animation while my sql proc is reading.

Something like picture.visible = true when its started and false when it ends.

1
  • And what did you find when you entered the error in Google? Commented Nov 24, 2011 at 14:49

5 Answers 5

4

Invoke is needed if you want to do this.

          private delegate void InvokeDelegate();

          public void DoSomething()
          {
               if (InvokeRequired)
               {
                    Invoke(new InvokeDelegate(DoSomething));
                    return;
               }
               // dosomething
          }

you can also add variables to the delegate and use them:

      private delegate void InvokeDelegate(string text);
      public void DoSomething(string text)
      {
           if (InvokeRequired)
           {
                Invoke(new InvokeDelegate(DoSomething), text);
                return;
           }
           // dosomething with text
      }

hope this helps :).

stefan

Sign up to request clarification or add additional context in comments.

1 Comment

Just a little remark about InvokeRequired, sometimes it can lie to you as explained here: ikriv.com/en/prog/info/dotnet/MysteriousHang.html. If your application hangs, at least you'll know where to look first ^_^
1

As others have pointed out, you cannot perform UI-related operations on a separate thread.

If you want your application to be responsive, you should perform the data operation on a separate thread instead.

If you just want to show the PictureBox control, you don't need the extra thread at all:

pictureBox2.Visible = true;
pictureBox2.Refresh(); // <-- causes the control to be drawn immediately
...large operation...
pictureBox2.Visible = false;

However if the user for instance alt-tabs back and forth, or drags another window over yours, the application would seem to hang as the UI thread is busy performing the data operation.

I'm suprised that so many people advise you to keep your current code and use InvokeRequired and Invoke, even though Invoke will only execute when the UI thread has time to process it (AFTER the data operation).

6 Comments

yeh, plus has it goes into the sql procedure wouldnt it stop the refresh? the picture box is an animation so the animation would be true but would not run?
Yes, no UI "action" is going to be performed as long as your data operation keeps the UI busy. If it's an animation you would have to process the data operation on a separate thread instead.
ok, so. i create a new class called MyThread in that class i instansiate the form1 class and use the picture animation set visible too true. then in my form class i call instantiate the thread and.....wait.... brains is to frazzled, how does OO work with threads?
I think OO and threading live in separate worlds, but your idea to put the data downloading code in a separate class sounds good (I'd name it something like BackgroundDownloader). Have a look at msdn.microsoft.com/en-us/library/ms951089.aspx for some examples and background information.
yeh thinking about OO and threads has cause me to have my 6th ciggerette in the last 2hrs! but yeh putting my data download into my thread called myclass. then leaves my form with direct access to the controls. but im wandering what would be the condition to stop the thread due to download data can have a different run time each time the user decideds to download so i need to be a bit dynamic and not hard code a "sleep"(i think thats the right call, not too sure).
|
1

You need to use InvokeRequired to access/modify a control from a thread other than the main thread of your form. Documentation here: http://msdn.microsoft.com/en-us/library/system.windows.forms.control.invokerequired.aspx

Comments

0

Have you tried with Tasks???

I make a simple test to show how I would make something similar (in WPF):

The XAML:

<Window x:Class="TaskLoading.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Button Content="Button" Height="23" HorizontalAlignment="Left" Margin="90,33,0,0" Name="button1" VerticalAlignment="Top" Width="75" Click="button1_Click" />
        <Image Height="118" HorizontalAlignment="Left" Margin="90,80,0,0" Name="imgLoading" Stretch="Fill" VerticalAlignment="Top" Width="122" Visibility="Hidden"  Source="/TaskLoading;component/loader_big.gif" />
    </Grid>
</Window>

The code behind:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Threading;
using System.Threading.Tasks;

namespace TaskLoading
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        public void bigProcess(){
            Thread.Sleep(5000);
        }

        private void button1_Click(object sender, RoutedEventArgs e)
        {
            imgLoading.Visibility = Visibility.Visible; //Make the icon visible.

            /* Start the bigProcess in a background thread: */
            Task changeIcon = Task.Factory.StartNew(() => 
                {
                    bigProcess();
                });

            /* At the end of the process make invisible the icon */
            changeIcon.ContinueWith((r) =>
            {
                imgLoading.Visibility = Visibility.Hidden;

            },
                TaskScheduler.FromCurrentSynchronizationContext()
            );
        }
    }
}

5 Comments

hmm looks interesting ill give it a try, what is a task btw? does it act like a synced function or thread?
ah, error needs an arg in the currentsynch method, Error 1 Delegate 'System.Action<object>' does not take 0 arguments
@StevenSmith I update my answer. The task create a thread that cannot modify the UI, but with TaskScheduler.FromCurrentSynchronizationContext() you can modify the UI.
thanks for the update, it has removed the error. but its does not show the picture box giff animation. ive never seen tasks before, could you reflect on why this would happen?
@StevenSmith I updated my answer to show an example, the bad part is that you need to show first your icon, then fill your DataSet in the method bigProcess() and at the end of your process hide your icon.
-2
    public void animateIcon()
    {
        Action action=()=>pictureBox2.Visible = true; 
        // animate
        this.Invoke(action);
    }

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.