C# invoke 线程回调 问题求教

时间:2022-08-27 23:34:40
初学C#,书上的例子,自己做了些改动,但是总感觉跟单线程似的,左边的框先输入完成,然后再执行右边框,自己看了好久没看出来哪里有问题,但是书上源代码就可以同时输入。。。

程序功能:用户在TextBox3中手动输入若干字符,选择复选框"任务1" or "任务2",然后点击提交,开启2个线程把TextBox3的内容按字符一个个输入到TextBox1和TextBox2中

C# invoke 线程回调 问题求教


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.Threading;

/*回调机制*/

namespace WindowsFormsApp_test
{
    public partial class Form1 : Form
    {
        private delegate void WriteTextBox(char ch); //定义委托
        private WriteTextBox writeTextBox1;  //声明委托
        private WriteTextBox writeTextBox2;

        //声明委托,以实现回调机制
        private delegate void WriteTextBoxCallback();
        //操作文本1的回调
        private WriteTextBoxCallback writeTexBox1Callback;
        //操作文本2的回调
        private WriteTextBoxCallback writeTexBox2Callback;

        public Form1()
        {
            InitializeComponent();
            //CheckForIllegalCrossThreadCalls = false;
            writeTexBox1Callback = new WriteTextBoxCallback(DowriteTextBox1);
            writeTexBox2Callback = new WriteTextBoxCallback(DowriteTextBox2);
        }

        private void button1_Click(object sender, EventArgs e)
        {
            //创建一个线程
            if (true == checkBox1.Checked)
            {
                ThreadStart doTask1 = new ThreadStart(DoTask1);
                Thread tskThread1 = new Thread(doTask1);
                tskThread1.Start();
            }

            if (true == checkBox2.Checked)
            {
                ThreadStart doTask2 = new ThreadStart(DoTask2);
                Thread tskThread2 = new Thread(doTask2);
                tskThread2.Start();
            }
        }

        private void DoTask1()
        {
            textBox1.Invoke(writeTexBox1Callback);
        }

        private void DoTask2()
        {
            textBox2.Invoke(writeTexBox2Callback);
        }

        private void WriteText(WriteTextBox wMethod)
        {
            string strdata = textBox3.Text;
            for (int i = 0; i < strdata.Length; i++)
            {
                wMethod(strdata[i]);
                DateTime now = DateTime.Now;
                while (now.AddMilliseconds(100) > DateTime.Now) { }
            }
        }

        private void DowriteTextBox1()
        {
            groupBox2.Text = "运行中...";
            groupBox2.Refresh();
            textBox1.Clear();
            textBox1.Refresh();

            writeTextBox1 = new WriteTextBox(WriteText1);
            WriteText(writeTextBox1);

            groupBox2.Text = "任务1";
        }

        private void DowriteTextBox2()
        {
            groupBox3.Text = "运行中...";
            groupBox3.Refresh();
            textBox2.Clear();
            textBox2.Refresh();

            writeTextBox2 = new WriteTextBox(WriteText2);
            WriteText(writeTextBox2);

            groupBox3.Text = "任务2";
        }

        //回调方法
        private void WriteText1(char ch)
        {
            textBox1.AppendText(ch + "\r");
        }

        private void WriteText2(char ch)
        {
            textBox2.AppendText(ch + "\r");
        }
    }
}


书上的原代码:

namespace WindowsFormsApp_test
{
    public partial class Form1 : Form
    {
        private delegate void WriteTextBox(char ch); //定义委托
        private WriteTextBox writeTextBox1;  //声明委托
        private WriteTextBox writeTextBox2;

        //声明委托,以实现回调机制
        private delegate void WriteTextBoxCallback(char ch);
        //操作文本1的回调
        private WriteTextBoxCallback writeTexBox1Callback;
        //操作文本2的回调
        private WriteTextBoxCallback writeTexBox2Callback;

        //操作"任务1"分组框的回调
        private delegate void ShowGroupBoxCallback(string str);
        private ShowGroupBoxCallback showGroupBox2Callback;
        //操作"任务2"分组框的回调
        private ShowGroupBoxCallback showGroupBox3Callback;

        public Form1()
        {
            InitializeComponent();
            //CheckForIllegalCrossThreadCalls = false;
            writeTexBox1Callback = new WriteTextBoxCallback(WriteText1);
            writeTexBox2Callback = new WriteTextBoxCallback(WriteText2);

            showGroupBox2Callback = new ShowGroupBoxCallback(ShGroup2);
            showGroupBox3Callback = new ShowGroupBoxCallback(ShGroup3);
        }

        private void button1_Click(object sender, EventArgs e)
        {
            //创建一个线程
            if (true == checkBox1.Checked)
            {
                groupBox2.Text = "运行中...";
                groupBox2.Refresh();
                textBox1.Clear();
                textBox1.Refresh();
                
                ThreadStart doTask1 = new ThreadStart(DoTask1);
                Thread tskThread1 = new Thread(doTask1);
                tskThread1.Start();
            }

            if (true == checkBox2.Checked)
            {
                groupBox3.Text = "运行中...";
                groupBox3.Refresh();
                textBox2.Clear();
                textBox2.Refresh();
                
                ThreadStart doTask2 = new ThreadStart(DoTask2);
                Thread tskThread2 = new Thread(doTask2);
                tskThread2.Start();
            }
        }

        private void DoTask1()
        {
            writeTextBox1 = new WriteTextBox(DowriteTextBox1);
            WriteText(writeTextBox1);
            //组合框回调
            groupBox2.Invoke(showGroupBox2Callback, "任务1");
        }

        private void DoTask2()
        {
            writeTextBox2 = new WriteTextBox(DowriteTextBox2);
            WriteText(writeTextBox2);
            groupBox3.Invoke(showGroupBox3Callback, "任务2");
        }

        private void WriteText(WriteTextBox wMethod)
        {
            string strdata = textBox3.Text;
            for (int i = 0; i < strdata.Length; i++)
            {
                wMethod(strdata[i]);
                DateTime now = DateTime.Now;
                while (now.AddMilliseconds(100) > DateTime.Now) { }
            }
        }

        private void DowriteTextBox1(char ch)
        {
            textBox1.Invoke(writeTexBox1Callback, ch);
        }

        private void DowriteTextBox2(char ch)
        {
            textBox2.Invoke(writeTexBox2Callback, ch);
        }
        
        //回调方法
        private void WriteText1(char ch)
        {
            textBox1.AppendText(ch + "\r");
        }

        private void WriteText2(char ch)
        {
            textBox2.AppendText(ch + "\r");
        }

        private void ShGroup2(string str)
        {
            groupBox2.Text = str;
        }
        private void ShGroup3(string str)
        {
            groupBox3.Text = str;
        }
    }
}

21 个解决方案

#1


Control的Invoke和BeginInvoke的委托方法是在主线程,即UI线程上执行的。也就是说如果你的委托方法用来取花费时间长的数据,然后更新界面什么的,千万别在UI线程上调用Control.Invoke和Control.BeginInvoke,因为这些是依然阻塞UI线程的,造成界面的假死。

#2


Control的BeginInvoke是相对于调用它的线程,即beginInvokeThread相对是异步的。
因此,我们可以想到。如果要异步取耗费长时间的数据,比如从数据库中读大量数据,我们应该这么做。
(1)如果你想阻止调用线程,那么调用代码(三),代码段D删掉,C改为耗费长时间的操作,因为这个操作是在另外一个线程中做的。代码段E改为更新界面的方法。
(2)如果你不想阻止调用线程,那么调用代码(四),代码段D删掉,C改为耗费长时间的操作,因为这个操作是在另外一个线程中做的。代码段E改为更新界面的方法。


http://www.cnblogs.com/mashang/archive/2009/08/01/1536730.html

#3


引用 1 楼 liucqa 的回复:
Control的Invoke和BeginInvoke的委托方法是在主线程,即UI线程上执行的。也就是说如果你的委托方法用来取花费时间长的数据,然后更新界面什么的,千万别在UI线程上调用Control.Invoke和Control.BeginInvoke,因为这些是依然阻塞UI线程的,造成界面的假死。

你的意思是说,我的代码DowriteTextBox1和DowriteTextBox2花费的时间太长导致的?

#4


而书上的代码WriteText1和WriteText2花费时间短??

#5


顶啊。。。。。

#6


该回复于2015-02-28 22:06:52被版主删除

#7



private void DowriteTextBox1()
        {
            groupBox2.Text = "运行中...";
            groupBox2.Refresh();
            textBox1.Clear();
            textBox1.Refresh();
 
            writeTextBox1 = new WriteTextBox(WriteText1);
            WriteText(writeTextBox1);
 
            groupBox2.Text = "任务1";
        }
 
        private void DowriteTextBox2()
        {
            groupBox3.Text = "运行中...";
            groupBox3.Refresh();
            textBox2.Clear();
            textBox2.Refresh();
 
            writeTextBox2 = new WriteTextBox(WriteText2);
            WriteText(writeTextBox2);
 
            groupBox3.Text = "任务2";
        }
 

这里写入控件操作是有问题的
应该改成

private void DowriteTextBox1()
        {
           
            writeTextBox1 = new WriteTextBox(WriteText1);
            WriteText(writeTextBox1);
 
        }
 
        private void DowriteTextBox2()
        {
        
            writeTextBox2 = new WriteTextBox(WriteText2);
            WriteText(writeTextBox2);
 
        }
 

#8


控件都是在主线程上运行的,委托是在另一个线程中运行的,你在另一个线程操作还操作主线程上的控件,就是会出问题的

#9


引用 8 楼 wl58796351 的回复:
控件都是在主线程上运行的,委托是在另一个线程中运行的,你在另一个线程操作还操作主线程上的控件,就是会出问题的

是通过Invoke调的啊。。

#10


引用 6 楼 liucqa 的回复:
有没有代码文件呀

http://download.csdn.net/detail/jingwei12345/7382707

#11


Invoke会阻塞主线程,你的代码用Thread执行Invoke,依然会阻塞主线程

原书中的例子每个Invoke只执行一个ch,另一个线程就能跟着操作ui了。两个线程轮流invoke一个ch,看着就像同步进行了

你的代码是Invoke执行全部的ch,自然阻塞住了,另一个线程只能等待全部的操作结束才能运行了

大概是这样吧

#12


我是这样实现的

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.Threading;

namespace WindowsFormsApplication16
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }


        private delegate void WriteRichTextBoxDelegate(string msg);
        WriteRichTextBoxDelegate wr1,wr2;

        private void writeRichTextBox1(string msg)
        {
            richTextBox1.AppendText(msg + "\r\n");
            richTextBox1.ScrollToCaret();
        }

        private void writeRichTextBox2(string msg)
        {
            richTextBox2.AppendText(msg + "\r\n");
            richTextBox2.ScrollToCaret();
        }

        private void writeRichTextBoxThread1()
        {
            while (true)
            {
                this.BeginInvoke(wr1, textBox1.Text);

                Thread.Sleep(1000);
            }
            
        }

        private void writeRichTextBoxThread2()
        {
            while (true)
            {
                this.BeginInvoke(wr2, textBox1.Text);
                Thread.Sleep(1000);
            }
            
        }
        private void button1_Click(object sender, EventArgs e)
        {

            Thread tskThread1=new Thread(new ThreadStart(writeRichTextBoxThread1));
            Thread tskThread2=new Thread(new ThreadStart(writeRichTextBoxThread2));
            tskThread1.Start();
            tskThread2.Start();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            wr1 = new WriteRichTextBoxDelegate(writeRichTextBox1);
            wr2 = new WriteRichTextBoxDelegate(writeRichTextBox2);
        }

    
    }
}

#13


就按照楼上所写。其实觉得可以优化下,没有必要非要写个委托方法,比如:


  Invoke((MethodInvoker) (() =>
                                        {
                                            txt_log.AppendText("--------------------成功【更新】实时库存数据,仓库: " +
                                                               Convert.ToInt32(_listOnStorage[0]) + "");
                                            txt_log.AppendText("\n");
                                        }));



其中 MethodInvoker 完全可以代替,感觉楼上写的有点复杂化。 

#14


你弄个附件吧,我这边调试不通过

Quote: 引用 12 楼 fstanwh 的回复:

我是这样实现的
[code=csharp]

#15


引用 13 楼 moranhuoshou 的回复:
就按照楼上所写。其实觉得可以优化下,没有必要非要写个委托方法,比如:


  Invoke((MethodInvoker) (() =>
                                        {
                                            txt_log.AppendText("--------------------成功【更新】实时库存数据,仓库: " +
                                                               Convert.ToInt32(_listOnStorage[0]) + "");
                                            txt_log.AppendText("\n");
                                        }));



其中 MethodInvoker 完全可以代替,感觉楼上写的有点复杂化。 


没看懂你的语法。。。
“ Invoke((MethodInvoker) (() =>”    这个是啥意思?

#16


引用 12 楼 fstanwh 的回复:
我是这样实现的

你这个跟我那代码有啥本质区别啊。
AppendText(msg + "\r\n");
只是我是按字符一个一个操作,你是一下子操作完。。

#17


你仔细看自己的代码跟书上的代码有啥区别.
书上操作groupbox也是用的委托,而你是直接操作的.
这就是区别所在.

#18


导致textbox虽然是异步操作的,但是在操作groupbox的时候是同步的
所以线程2要等线程1全部操作完成了才能运行

#19


或者你就是不想用委托去操作groupbox,就把操作groupbox的代码写到线程函数的外面去,不要写到线程里.

#20


引用 15 楼 jingwei12345 的回复:
Quote: 引用 13 楼 moranhuoshou 的回复:

就按照楼上所写。其实觉得可以优化下,没有必要非要写个委托方法,比如:


  Invoke((MethodInvoker) (() =>
                                        {
                                            txt_log.AppendText("--------------------成功【更新】实时库存数据,仓库: " +
                                                               Convert.ToInt32(_listOnStorage[0]) + "");
                                            txt_log.AppendText("\n");
                                        }));



其中 MethodInvoker 完全可以代替,感觉楼上写的有点复杂化。 


没看懂你的语法。。。
“ Invoke((MethodInvoker) (() =>”    这个是啥意思?



MethodInvoker你不知道是什么意思,可以复制到代码里 看下注释,后面()=> 是个lamba 表达方法。可以省去你定义一个一个delegate。。。。 

#21


引用 19 楼 Z65443344 的回复:
或者你就是不想用委托去操作groupbox,就把操作groupbox的代码写到线程函数的外面去,不要写到线程里.


        private void button1_Click(object sender, EventArgs e)
        {
            //创建一个线程
            if (true == checkBox1.Checked)
            {
                groupBox2.Text = "运行中...";
                groupBox2.Refresh();
                textBox1.Clear();
                textBox1.Refresh();

                ThreadStart doTask1 = new ThreadStart(DoTask1);
                Thread tskThread1 = new Thread(doTask1);
                tskThread1.Start();

                groupBox2.Text = "任务1";
            }

            if (true == checkBox2.Checked)
            {
                groupBox3.Text = "运行中...";
                groupBox3.Refresh();
                textBox2.Clear();
                textBox2.Refresh();

                ThreadStart doTask2 = new ThreadStart(DoTask2);
                Thread tskThread2 = new Thread(doTask2);
                tskThread2.Start();

                groupBox3.Text = "任务2";
            }
        }

private void DowriteTextBox1()
        {
            writeTextBox1 = new WriteTextBox(WriteText1);
            WriteText(writeTextBox1);
        }

        private void DowriteTextBox2()
        {
            writeTextBox2 = new WriteTextBox(WriteText2);
            WriteText(writeTextBox2);
        }


这个我试过了,也不行。。。哎,还是按书上的来吧。。。

#1


Control的Invoke和BeginInvoke的委托方法是在主线程,即UI线程上执行的。也就是说如果你的委托方法用来取花费时间长的数据,然后更新界面什么的,千万别在UI线程上调用Control.Invoke和Control.BeginInvoke,因为这些是依然阻塞UI线程的,造成界面的假死。

#2


Control的BeginInvoke是相对于调用它的线程,即beginInvokeThread相对是异步的。
因此,我们可以想到。如果要异步取耗费长时间的数据,比如从数据库中读大量数据,我们应该这么做。
(1)如果你想阻止调用线程,那么调用代码(三),代码段D删掉,C改为耗费长时间的操作,因为这个操作是在另外一个线程中做的。代码段E改为更新界面的方法。
(2)如果你不想阻止调用线程,那么调用代码(四),代码段D删掉,C改为耗费长时间的操作,因为这个操作是在另外一个线程中做的。代码段E改为更新界面的方法。


http://www.cnblogs.com/mashang/archive/2009/08/01/1536730.html

#3


引用 1 楼 liucqa 的回复:
Control的Invoke和BeginInvoke的委托方法是在主线程,即UI线程上执行的。也就是说如果你的委托方法用来取花费时间长的数据,然后更新界面什么的,千万别在UI线程上调用Control.Invoke和Control.BeginInvoke,因为这些是依然阻塞UI线程的,造成界面的假死。

你的意思是说,我的代码DowriteTextBox1和DowriteTextBox2花费的时间太长导致的?

#4


而书上的代码WriteText1和WriteText2花费时间短??

#5


顶啊。。。。。

#6


该回复于2015-02-28 22:06:52被版主删除

#7



private void DowriteTextBox1()
        {
            groupBox2.Text = "运行中...";
            groupBox2.Refresh();
            textBox1.Clear();
            textBox1.Refresh();
 
            writeTextBox1 = new WriteTextBox(WriteText1);
            WriteText(writeTextBox1);
 
            groupBox2.Text = "任务1";
        }
 
        private void DowriteTextBox2()
        {
            groupBox3.Text = "运行中...";
            groupBox3.Refresh();
            textBox2.Clear();
            textBox2.Refresh();
 
            writeTextBox2 = new WriteTextBox(WriteText2);
            WriteText(writeTextBox2);
 
            groupBox3.Text = "任务2";
        }
 

这里写入控件操作是有问题的
应该改成

private void DowriteTextBox1()
        {
           
            writeTextBox1 = new WriteTextBox(WriteText1);
            WriteText(writeTextBox1);
 
        }
 
        private void DowriteTextBox2()
        {
        
            writeTextBox2 = new WriteTextBox(WriteText2);
            WriteText(writeTextBox2);
 
        }
 

#8


控件都是在主线程上运行的,委托是在另一个线程中运行的,你在另一个线程操作还操作主线程上的控件,就是会出问题的

#9


引用 8 楼 wl58796351 的回复:
控件都是在主线程上运行的,委托是在另一个线程中运行的,你在另一个线程操作还操作主线程上的控件,就是会出问题的

是通过Invoke调的啊。。

#10


引用 6 楼 liucqa 的回复:
有没有代码文件呀

http://download.csdn.net/detail/jingwei12345/7382707

#11


Invoke会阻塞主线程,你的代码用Thread执行Invoke,依然会阻塞主线程

原书中的例子每个Invoke只执行一个ch,另一个线程就能跟着操作ui了。两个线程轮流invoke一个ch,看着就像同步进行了

你的代码是Invoke执行全部的ch,自然阻塞住了,另一个线程只能等待全部的操作结束才能运行了

大概是这样吧

#12


我是这样实现的

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.Threading;

namespace WindowsFormsApplication16
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }


        private delegate void WriteRichTextBoxDelegate(string msg);
        WriteRichTextBoxDelegate wr1,wr2;

        private void writeRichTextBox1(string msg)
        {
            richTextBox1.AppendText(msg + "\r\n");
            richTextBox1.ScrollToCaret();
        }

        private void writeRichTextBox2(string msg)
        {
            richTextBox2.AppendText(msg + "\r\n");
            richTextBox2.ScrollToCaret();
        }

        private void writeRichTextBoxThread1()
        {
            while (true)
            {
                this.BeginInvoke(wr1, textBox1.Text);

                Thread.Sleep(1000);
            }
            
        }

        private void writeRichTextBoxThread2()
        {
            while (true)
            {
                this.BeginInvoke(wr2, textBox1.Text);
                Thread.Sleep(1000);
            }
            
        }
        private void button1_Click(object sender, EventArgs e)
        {

            Thread tskThread1=new Thread(new ThreadStart(writeRichTextBoxThread1));
            Thread tskThread2=new Thread(new ThreadStart(writeRichTextBoxThread2));
            tskThread1.Start();
            tskThread2.Start();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            wr1 = new WriteRichTextBoxDelegate(writeRichTextBox1);
            wr2 = new WriteRichTextBoxDelegate(writeRichTextBox2);
        }

    
    }
}

#13


就按照楼上所写。其实觉得可以优化下,没有必要非要写个委托方法,比如:


  Invoke((MethodInvoker) (() =>
                                        {
                                            txt_log.AppendText("--------------------成功【更新】实时库存数据,仓库: " +
                                                               Convert.ToInt32(_listOnStorage[0]) + "");
                                            txt_log.AppendText("\n");
                                        }));



其中 MethodInvoker 完全可以代替,感觉楼上写的有点复杂化。 

#14


你弄个附件吧,我这边调试不通过

Quote: 引用 12 楼 fstanwh 的回复:

我是这样实现的
[code=csharp]

#15


引用 13 楼 moranhuoshou 的回复:
就按照楼上所写。其实觉得可以优化下,没有必要非要写个委托方法,比如:


  Invoke((MethodInvoker) (() =>
                                        {
                                            txt_log.AppendText("--------------------成功【更新】实时库存数据,仓库: " +
                                                               Convert.ToInt32(_listOnStorage[0]) + "");
                                            txt_log.AppendText("\n");
                                        }));



其中 MethodInvoker 完全可以代替,感觉楼上写的有点复杂化。 


没看懂你的语法。。。
“ Invoke((MethodInvoker) (() =>”    这个是啥意思?

#16


引用 12 楼 fstanwh 的回复:
我是这样实现的

你这个跟我那代码有啥本质区别啊。
AppendText(msg + "\r\n");
只是我是按字符一个一个操作,你是一下子操作完。。

#17


你仔细看自己的代码跟书上的代码有啥区别.
书上操作groupbox也是用的委托,而你是直接操作的.
这就是区别所在.

#18


导致textbox虽然是异步操作的,但是在操作groupbox的时候是同步的
所以线程2要等线程1全部操作完成了才能运行

#19


或者你就是不想用委托去操作groupbox,就把操作groupbox的代码写到线程函数的外面去,不要写到线程里.

#20


引用 15 楼 jingwei12345 的回复:
Quote: 引用 13 楼 moranhuoshou 的回复:

就按照楼上所写。其实觉得可以优化下,没有必要非要写个委托方法,比如:


  Invoke((MethodInvoker) (() =>
                                        {
                                            txt_log.AppendText("--------------------成功【更新】实时库存数据,仓库: " +
                                                               Convert.ToInt32(_listOnStorage[0]) + "");
                                            txt_log.AppendText("\n");
                                        }));



其中 MethodInvoker 完全可以代替,感觉楼上写的有点复杂化。 


没看懂你的语法。。。
“ Invoke((MethodInvoker) (() =>”    这个是啥意思?



MethodInvoker你不知道是什么意思,可以复制到代码里 看下注释,后面()=> 是个lamba 表达方法。可以省去你定义一个一个delegate。。。。 

#21


引用 19 楼 Z65443344 的回复:
或者你就是不想用委托去操作groupbox,就把操作groupbox的代码写到线程函数的外面去,不要写到线程里.


        private void button1_Click(object sender, EventArgs e)
        {
            //创建一个线程
            if (true == checkBox1.Checked)
            {
                groupBox2.Text = "运行中...";
                groupBox2.Refresh();
                textBox1.Clear();
                textBox1.Refresh();

                ThreadStart doTask1 = new ThreadStart(DoTask1);
                Thread tskThread1 = new Thread(doTask1);
                tskThread1.Start();

                groupBox2.Text = "任务1";
            }

            if (true == checkBox2.Checked)
            {
                groupBox3.Text = "运行中...";
                groupBox3.Refresh();
                textBox2.Clear();
                textBox2.Refresh();

                ThreadStart doTask2 = new ThreadStart(DoTask2);
                Thread tskThread2 = new Thread(doTask2);
                tskThread2.Start();

                groupBox3.Text = "任务2";
            }
        }

private void DowriteTextBox1()
        {
            writeTextBox1 = new WriteTextBox(WriteText1);
            WriteText(writeTextBox1);
        }

        private void DowriteTextBox2()
        {
            writeTextBox2 = new WriteTextBox(WriteText2);
            WriteText(writeTextBox2);
        }


这个我试过了,也不行。。。哎,还是按书上的来吧。。。

相关文章