C# thread on forms
Possible Duplicate:
How to update GUI from another thread in C#?
我现在有一个C程序来运行一个查询并在一个
由于记录大小的原因,查询需要运行一段时间(20-30秒)。
我想我会添加一个动画,这样用户至少知道软件正在运行,并且没有停止工作。
当然,当调用过程时,我不能运行任何东西,所以我研究了线程。
这是我的代码(请原谅,我还没有真正发表评论):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 | 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: " + 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: " + 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过程读取时执行动画。
有点像
如果要执行此操作,则需要调用。
1 2 3 4 5 6 7 8 9 10 11 | private delegate void InvokeDelegate(); public void DoSomething() { if (InvokeRequired) { Invoke(new InvokeDelegate(DoSomething)); return; } // dosomething } |
还可以向委托添加变量并使用它们:
1 2 3 4 5 6 7 8 9 10 | private delegate void InvokeDelegate(string text); public void DoSomething(string text) { if (InvokeRequired) { Invoke(new InvokeDelegate(DoSomething), text); return; } // dosomething with text } |
希望这有帮助:)。
斯特凡
正如其他人指出的,您不能在单独的线程上执行与UI相关的操作。
如果希望应用程序具有响应性,则应在单独的线程上执行数据操作。
如果只想显示PictureBox控件,则根本不需要额外的线程:
1 2 3 4 | pictureBox2.Visible = true; pictureBox2.Refresh(); // <-- causes the control to be drawn immediately ...large operation... pictureBox2.Visible = false; |
但是,如果用户(例如alt)来回切换选项卡,或者在您的窗口上拖动另一个窗口,那么当UI线程忙于执行数据操作时,应用程序似乎会挂起。
我很惊讶,这么多人建议您保留当前代码并使用
您需要使用invokeRequired从窗体的主线程以外的线程访问/修改控件。文档位于:http://msdn.microsoft.com/en-us/library/system.windows.forms.control.invokeRequired.aspx
你试过
我做了一个简单的测试来展示我将如何制作类似的东西(在wpf中):
XAML:
1 2 3 4 5 6 7 8 9 | <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> |
背后的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | 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() ); } } } |
1 2 3 4 5 6 | public void animateIcon() { Action action=()=>pictureBox2.Visible = true; // animate this.Invoke(action); } |