2011-11-24 24 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