C# 跨类 跨线程 更新界面

经常要用到C#子线程中更新界面,以前都是要用到了找一找,大部分都是窗体类的子线程去访问,比较少遇到在另外一个类里面去更新窗体。

下面先介绍一下理论知识。参考文章来源:

https://www.cnblogs.com/dzw2017/p/7479477.html

https://www.cnblogs.com/TankXiao/p/3348292.html

https://blog.csdn.net/kellygod/article/details/75452932

……

在C#应用程序开发中,我们经常需要把UI线程和工作线程分开编程,为了防止界面停止响应。同时,我们也需要在工作线程中去更新UI界面的控件,在CLR的线程安全中并不允许我们直接在工作线程操作UI界面。因此,介绍以下三种方式进行跨线程操作UI。

第一种方法:使用delegate和invoke来从其他线程中调用控件

private void button2_Click(object sender, EventArgs e)
        {
            Thread thread1 = new Thread(new ParameterizedThreadStart(UpdateLabel2));
            thread1.Start("更新Label");
        }

        private void UpdateLabel2(object str)
        {
        // 当一个控件的InvokeRequired属性值为真时,说明有一个创建它以外的线程想访问它
            if (label2.InvokeRequired)
            {
      //lamda表达式的action委托,<string>为参数类型,(x)为形参
                Action<string> actionDelegate = (x) => { this.label2.Text = x.ToString(); };
                // 或者
                // Action<string> actionDelegate = delegate(string txt) { this.label2.Text = txt; };
     //同步激活委托action
                this.label2.Invoke(actionDelegate, str);
            }
            else
            {
                this.label2.Text = str.ToString();
            }
        }

第二种方法:使用delegate和BeginInvoke来从其他线程中控制控件

      该方法与上述方法的唯一差别在于,其中的Invoke激活函数换成了BeginInvoke激活函数。 两个函数的本质区别在于,Invoke方法是线程同步,当工作线程执行完毕后,才会再次触发;  而BeginInvoke方法是线程异步,当工作线程还未执行完,它便会开启另一个线程去完成工作线程。

private void button4_Click(object sender, EventArgs e)
        {
            using (BackgroundWorker bw = new BackgroundWorker())
            {
                bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
                bw.DoWork += new DoWorkEventHandler(bw_DoWork);
                bw.RunWorkerAsync("Tank");
            }         
        }
    //做耗时工作线程
        void bw_DoWork(object sender, DoWorkEventArgs e)
        {       
            // 这里是后台线程, 是在另一个线程上完成的
            // 这里是真正做事的工作线程
            // 可以在这里做一些费时的,复杂的操作
            Thread.Sleep(5000);
            e.Result = e.Argument + "工作线程完成";
        }
    //回到UI主线程
        void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            //这时后台线程已经完成,并返回了主线程,所以可以直接使用UI控件了 
            this.label4.Text = e.Result.ToString(); 
        }

下面介绍自己所用的方法。

背景:窗体main中,定时执行类DataManipulate中方法BeginWork(),BeginWork()会开辟多个线程执行方法QuerySingleCompany();在QuerySingleCompany中需要更新main中RichTextBox(name:rtblog),添加日志消息文本。

首先窗体类main.cs

#define Test
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;

using System.Text;
using System.Threading.Tasks;
using System.Timers;
using System.Windows.Forms;

namespace LvWangData
{
    public partial class Main : Form
    {
        private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
        DataManipulate dm = new DataManipulate();
        public Main()
        {
            InitializeComponent();
            #region 非定时 测试用
#if Debug
            //dm = new DataManipulate();
            //dm.LogEventHandler += new DataManipulate.LogHandler(Dm_LogEventHandler);
            //dm.BeginWork();
#endif

            #endregion

        }
        System.Timers.Timer timer = new System.Timers.Timer();
        private void btnQueryOnTime_Click(object sender, EventArgs e)
        {
#if Test
            BaseDB.Model.LvWangInfo lw = new BaseDB.Model.LvWangInfo();
            lw.CompanyID = "E9855A6C-3F1A-4AA0-9AAB-DE3AF2E10031";
            lw.IsValid = true;
            lw.LastQueryTime = Convert.ToDateTime("2019-02-13 00:00:00.000");
            lw.LvwangID = 942277;
            DataManipulate dm = new DataManipulate();
            dm.LogEventHandler += new DataManipulate.LogHandler(Dm_LogEventHandler);
            dm.OpenTestFunc(lw);
#elif Deploy

            if (btnQueryOnTime.Text.Equals("开启定时查询"))
            {
                //System.Timers.Timer timer = new System.Timers.Timer();
                timer.Enabled = true;
                timer.Interval = 55000;//执行间隔时间,单位为毫秒;此时时间间隔为1分钟  
                timer.Start();
                timer.Elapsed += new System.Timers.ElapsedEventHandler(QueryOnTime);
                btnQueryOnTime.Text = "关闭定时查询";
            }
            else
            {
                timer.Close();
                btnQueryOnTime.Text = "开启定时查询";
            }
#endif

        }
        /// <summary>
        /// 每个1小时查询一次
        /// </summary>
        /// <param name="source"></param>
        /// <param name="e"></param>
        private void QueryOnTime(object source, ElapsedEventArgs e)
        {
            int min = Convert.ToInt32(Configs.GetValue("Min"));

            if (DateTime.Now.Minute == min)
            {
                dm = new DataManipulate();
                dm.LogEventHandler += new DataManipulate.LogHandler(Dm_LogEventHandler);
                BackgroundWorker bgw = new BackgroundWorker();

                // 线程执行的内容
                bgw.DoWork += new DoWorkEventHandler(bgw_DoWork);
                bgw.RunWorkerAsync();



            }
        }
        void bgw_DoWork(object sender, DoWorkEventArgs e)
        {
            dm.BeginWork();

        }

        private void Dm_LogEventHandler(string logstr)
        {
            if (rtblog.InvokeRequired)
            {
                // 当一个控件的InvokeRequired属性值为真时,说明有一个创建它以外的线程想访问它
                Action<string> actionDelegate = (x) => { this.rtblog.AppendText(x.ToString() + "\r"); };
                // 或者
                // Action<string> actionDelegate = delegate(string txt) { this.label2.Text = txt; };
                this.rtblog.BeginInvoke(actionDelegate, logstr);
            }
            else
            {
                this.rtblog.AppendText(logstr.ToString() + "\r");
            }
        }

        public void UpdateRtbLog(object str)
        {
            if (rtblog.InvokeRequired)
            {
                // 当一个控件的InvokeRequired属性值为真时,说明有一个创建它以外的线程想访问它
                Action<string> actionDelegate = (x) =>
                {
                    if (this.rtblog.TextLength > 102400)
                    {
                        this.rtblog.Clear();
                    }
                    this.rtblog.AppendText(x.ToString());
                };
                // 或者
                // Action<string> actionDelegate = delegate(string txt) { this.label2.Text = txt; };
                this.rtblog.Invoke(actionDelegate, str);
            }
            else
            {
                this.rtblog.AppendText(str.ToString());
            }
        }
    }
}

在DataManipulate.cs中,只留下了关键代码

using LvWangData.Entity;
using Maticsoft.DBUtility;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace LvWangData
{
    public class DataManipulate
    {
        private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();

        public delegate void LogHandler(string logstr);

        /// <summary>
        /// 定义捕获到socket事件,类型AcceptSocketHandler
        /// </summary>
        public event LogHandler LogEventHandler;

      

        public DataManipulate()
        {
            logger.Info("进入DataManipulate");

            if (LogEventHandler != null)
            {
                LogEventHandler("进入DataManipulate");
            }

        }
        /// <summary>
        /// 单独分出来,开始工作
        /// </summary>
        public void BeginWork()
        {
            Init();
            if (LogEventHandler != null)
            {
                LogEventHandler("BeginWork()");
            }
            //获取LvWangInfo表中数据,每一家公司开辟一个线程进行数据查询入库
            if (_lvwanginfolist != null)
            {
                foreach (BaseDB.Model.LvWangInfo lw in _lvwanginfolist)
                {
                    Thread.Sleep((new Random()).Next(100, 2000));
                    Thread t = new Thread(new ParameterizedThreadStart(QuerySingleCompany));
                    t.Start(lw);
                }
            }
        }
        private void Init()
        {
            try
            {
               

                StringBuilder sb = new StringBuilder();
                sb.Append(DateTime.Now.ToString() + "获取绿网公司信息:\r");
                foreach (BaseDB.Model.LvWangInfo info in _lvwanginfolist)
                {
                    sb.Append(info.CompanyID + "\t" + info.LastQueryTime +"\r");
                }
                LogEventHandler(sb.ToString());
                logger.Info(sb.ToString());
                LogEventHandler("Init Over");
            }
            catch (Exception e)
            {
                logger.Error("Init\t" + e.StackTrace);
                LogEventHandler(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "\tInit\t" + e.StackTrace);
            }

        }
       

    }
}

 

展开阅读全文

没有更多推荐了,返回首页