现在我想写一个带有界面的c#程序,当然我要采用UI部分和程序的实现部分分离的办法,现在我写了一个线程类,在UI启动时创建线程,这样两部分就可以分离工作了。
但是,在UI中要向子线程中传递信息,并且在子线程中处理完任务也要向UI返回信息。
问题:
    UI与子线程的信息传递应该如何实现?
    如果采用发送消息的方式,那么子线程向UI发送信息,UI要怎样做才能接受到子线程的消息?做好有代码实例。大虾帮忙,不胜感激。

解决方案 »

  1.   

    感谢dancingbit,我刚学C#,有些地方还不太懂,能否给个简单的代码例子?
      

  2.   

    backgroundworker组件 msdn看看吧
      

  3.   

    public class Form1:Form
    {
      private Thread t=null;
       ...
      public class ThreadClass
      {
         private bool isRunning=false;
         ...
         public bool IsRunning
         { 
           set{...}
           get{...}
         }
          ...
          public void ThreadProc()
          {
           ...
          }
        }
    }
    大概的框架是如此,创建线程之前,先创建一个ThreadClass的实例,通过这个实例来启动线程,通过实例的属性来与线程交换信息。
      

  4.   

    比较简单但是不安全的方法是:    Control.CheckForIllegalCrossThreadCalls = false;//关闭线程互访的限制机制,目的 新线程可以访问主线程的资源
    安全的方法是:代理
    一、多线程实例,一个将域名解析成IP地址的程序:建立一个C# winform 程序 添加一个 timer 、一个progressBar、一个textbox、两个Button;将form的带面替换成以下代码:using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Text;
    using System.Windows.Forms;
    using System.Threading;
    using System.Net ;
    namespace ThreadTest
    {
        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
            }        private void Form1_Load(object sender, EventArgs e)
            {
                Control.CheckForIllegalCrossThreadCalls = false;//关闭线程互访的限制机制,目的 新线程可以访问主线程的文本框控件        }
                 public static string newstr (string str) //获取ip地址函数
            {
                try
                {                IPHostEntry ip = new IPHostEntry(); 
                    ip = Dns.GetHostEntry(str);                String ips="";
                    for (int i = 0; i < ip.AddressList.Length; i++)
                    {
                        ips = ips + ip.AddressList[i].ToString() + ";";
                    }
                    return ips.ToString();
                }
                catch(Exception err )
                {  return err.ToString(); }
            }
            
             private void tex() //执行 获取 地址的过程
             {
                 textBox1.Text = newstr(textBox1.Text.ToString());
                 MessageBox.Show(newstr(textBox1.Text.ToString()).ToString());         }        private void timer1_Tick(object sender, EventArgs e)//time1控制进度条不断增长循环
            {
                if (progressBar1.Value < 100)
                {
                    progressBar1.Value = progressBar1.Value + 1;
                }
                else
                {
                    progressBar1.Value = 0;
                }
            }
            private void button1_Click(object sender, EventArgs e) //按钮1的单击事件,使用线程
            {
                Thread t1 = new Thread(tex);  //定义新线程
                t1.Start();//开始            timer1.Enabled = true;//打开计时器
            }
            private void button2_Click(object sender, EventArgs e)//按钮2的单击事件 ,不使用线程
            {
                tex();
                timer1.Enabled = true;        }
               }
    } F5运行程序,在文版框内填入想要解析的 域名(最好是不存在的域名)  单击 Button1  可以看到 界面无停顿的解析结果;单击Button2 可以看到界面 有明显停顿。 二、代理(Delegate)C#中默认的情况下 线程是不允许互访的,为了安全性和稳定性,代码:private void Form1_Load(object sender, EventArgs e)
            {
                Control.CheckForIllegalCrossThreadCalls = false;//关闭线程互访的限制机制,目的 新线程可以访问主线程的文本框控件        }
         可以关闭 此检查,但是此种方法  不安全,故使用 代理的方式 进行线程互访。代码如下: using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Text;
    using System.Windows.Forms;
    using System.Threading;
    using System.Net ;
    namespace ThreadTest
    {
        public partial class Form1 : Form
        {
           
            public Form1()
            {
                InitializeComponent();
            }        delegate void aa();//创建一个代理
            public string s;//公共变量,返回线程执行的结果与主线程共享。
            private void Form1_Load(object sender, EventArgs e)
            {
               //Control.CheckForIllegalCrossThreadCalls = false;//关闭线程互访的限制机制,目的 新线程可以访问主线程的文本框控件        }
              
            public static string newstr (string str) //获取ip地址函数
            {
                Thread.Sleep(5000);
                try
                {                IPHostEntry ip = new IPHostEntry(); 
                    ip = Dns.GetHostEntry(str);                String ips="";
                    for (int i = 0; i < ip.AddressList.Length; i++)
                    {
                        ips = ips + ip.AddressList[i].ToString() + ";";
                    }
                    return ips.ToString();
                }
                catch(Exception err )
                {  return err.ToString(); }
            }
           
            private void  setw()
            {
                textBox1.Text = s;
            }
             private void tex() //执行 获取 地址的过程
            {
                 s=newstr(textBox1.Text.ToString());
                 textBox1.BeginInvoke(new aa(setw));
                
             }        private void timer1_Tick(object sender, EventArgs e)//time1控制进度条不断增长循环
            {
                if (progressBar1.Value < 100)
                {
                    progressBar1.Value = progressBar1.Value + 1;
                }
                else
                {
                    progressBar1.Value = 0;
                }
            }
            private  void changeText(string s)
            {
                textBox1.Text = s;
            }        private void button1_Click(object sender, EventArgs e) //按钮1的单击事件,使用线程
            {
                Thread t1 = new Thread(tex);  //定义新线程
                t1.Start();//开始
               timer1.Enabled = true;//打开计时器
            }
            private void button2_Click(object sender, EventArgs e)//按钮2的单击事件 ,不使用线程
            {
                tex();
                timer1.Enabled = true;        }
               }
    }   其他:多线程访问主界面控件的方式就是 调用主线程的 一个方法来改变,我理解具体操作是有主线程完成的。实例2:using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Text;
    using System.Windows.Forms;
    using System.Net;namespace WindowsFormsApplication2
    {
        public partial class Form1 : Form
        {
            public delegate void 我的委托();//声明一个委托
            public string s;//定义公共变量 ,方便线程共享。
            public Form1()
            {
                InitializeComponent();
            }
            public static string newstr(string str) //获取ip地址函数-一个耗时的操作
            {
                try
                {                IPHostEntry ip = new IPHostEntry();
                    ip = Dns.GetHostEntry(str);                String ips = "";
                    for (int i = 0; i < ip.AddressList.Length; i++)
                    {
                        ips = ips + ip.AddressList[i].ToString() + ";";
                    }
                    return ips.ToString();
                }
                catch (Exception err)
                { return err.ToString(); }
            }
                   
            public void 设置文字() //定义一个改变Button文字的方法
            {
                button1.Text = s;
            }        public void 新线程() 
            {
                s = newstr("111a");
                System.Threading.Thread.Sleep(5000);//线程休眠2秒
                button1.BeginInvoke(new 我的委托(设置文字));//在button1所在线程上执行“设置文字”,这里的 设置文字函数必须 无参
            }        private void button1_Click_1(object sender, EventArgs e)
            {
                new System.Threading.Thread(new System.Threading.ThreadStart(新线程)).Start();//创建一个新的线程并启动        }        private void timer1_Tick(object sender, EventArgs e) //控制进度条 显示  方便判断界面响应情况
            {
                if (progressBar1.Value < 100)
                {
                    progressBar1.Value = progressBar1.Value + 1;
                }
                else
                {
                    progressBar1.Value = 0;
                }        }        private void Form1_Load(object sender, EventArgs e)
            {
                timer1.Enabled = true;
            }
        }}
     
      

  5.   

    TO dancingbit:
          谢谢解答,但是还是存在一个问题。
          我的程序向实现一个下载的功能,当点击UI上的【开始】Button后,【开始】变灰色,UI传递下载地址给后台下载线程,当后台线程下载完成后,再通知UI,UI将【开始】Button还原。
          采用你的方法后,UI可以将下载地址传递给后台线程,但是当后台通知UI下载完成时,UI不能立即响应这个下载完成的消息,请问怎样可以让UI立即响应,将【开始】Button还原为正常状态?TO bugers:
        谢谢解答,但是还是存在一个问题。
        我的程序想建立的线程类,当UI启动的时候实例化这个线程类,但是你的代码中的线程是一个函数,该如何在线程类中用托管,我不太懂,可不可以再给个线程类的代码?TO LQknife
        这个方法没太看懂,正在看ing
      

  6.   

    1.需要立即响应的话,可以在内部类中定义一个事件,当下载完成后触发这个事件。主窗体的代码中订阅此事件,在响应代码中激活相应按钮。
    2.不安全的方法,Control.CheckForIllegalCrossThreadCalls = false;然后在线程类中调用主窗体中某个方法改变按钮的状态。关于自定义事件,可参考:http://topic.csdn.net/u/20090805/16/41a8b980-02bb-4e6e-86d0-26967480b76f.html