2011-11-24 25 views
1

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

我現在有一個C#程序運行一個查詢並顯示在datagridview結果。

由於記錄大小的查詢需要一段時間(20-30秒)才能運行。

我想我會添加一個動畫,以便用戶至少知道該軟件正在運行,並沒有停止工作。

當程序正在進行調用時,我無法運行任何東西,因此我查看了線程。

這裏是我的代碼(原諒我,我還沒有真正落實評論):

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; 
     } 
    } 
} 

正如你可以看到我只想過程調用之前運行的動畫,然後就結束吧後。

我的線程知識是全新的。我環顧四周,但目前我有點困惑。

這裏是我的錯誤:

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"

我需要的,而我的SQL proc已經閱讀進行動畫的一個非常簡單的方法。

類似於picture.visible = true開始時的狀態,當它結束時爲false。

+0

當你在Google中輸入錯誤時,你發現了什麼? – CodeCaster

回答

4

如果你想這樣做,需要調用。

  private delegate void InvokeDelegate(); 

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

你也可以變量添加到委託並使用它們:

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

希望這有助於:)。

斯特凡

+0

關於'InvokeRequired'的一點評論,有時候可以在這裏解釋:http://www.ikriv.com/en/prog/info/dotnet/MysteriousHang.html。如果你的應用程序掛起,至少你會知道在哪裏看第一個^ _^ –

-2
public void animateIcon() 
    { 
     Action action=()=>pictureBox2.Visible = true; 
     // animate 
     this.Invoke(action); 
    } 
1

正如其他人所指出的那樣,你不能在一個單獨的線程中執行UI相關的操作。

如果您希望您的應用程序具有響應性,您應該在單獨的線程上執行數據操作。

如果你只是想顯示PictureBox控件,你不需要額外的線程都:

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

但是,如果用戶例如ALT-標籤來回,或通過拖動另一個窗口你的應用程序似乎會掛起,因爲UI線程忙於執行數據操作。

我驚訝有那麼多人勸你保持當前的代碼,並使用InvokeRequiredInvoke,即使當UI線程有時間來處理它(數據操作之後)Invoke將只執行。

+0

是的,加上它進入SQL過程不會停止刷新?圖片框是一個動畫,所以動畫將是真實的,但不會運行? – lemunk

+0

是的,只要您的數據操作使UI保持繁忙狀態,就不會執行UI「操作」。如果是動畫,則必須在單獨的線程上處理數據操作。 –

+0

好的,所以。 我創建了一個名爲MyThread的新類,在這個類中,我實例化了form1類,並使用可見的圖片動畫設置爲true。 然後在我的窗體類中,我調用了實例化線程,並且.....等待.... 大腦是疲憊的,OO如何與線程一起工作? – lemunk

0

您是否試過Task s ???

我做一個簡單的測試來證明我會怎麼做(在WPF)類似的東西:

的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> 

背後的代碼:

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() 
      ); 
     } 
    } 
} 
+0

嗯看起來有趣的生病給它一個嘗試,什麼是任務btw?它是否像一個同步函數或線程? – lemunk

+0

啊,錯誤需要一個ARG在currentsynch方法, 錯誤委託「System.Action 」不拿0參數 – lemunk

+0

@StevenSmith更新我的答案。該任務創建一個無法修改UI的線程,但使用'TaskScheduler.FromCurrentSynchronizationContext()'您可以修改UI。 – Galled