c#文本框,不同类,backgroundWorker

Posted

技术标签:

【中文标题】c#文本框,不同类,backgroundWorker【英文标题】:c# text box, different class, backgroundWorker 【发布时间】:2016-12-05 13:14:40 【问题描述】:

我是 c# 新手,有一个简单的项目,我有一个 Form1,它有一个打开按钮 Form2(诊断页面稍后将受密码保护)。 我创建了一个SerialPortClass,你可以猜到它处理所有的串口方法,例如 openport、sendline、isPortOpen 等。 我想要做的是从SerialPortClass 的串口接收一个串行字符串,然后在Form2 的文本框中显示这个字符串。在阅读了本网站和其他网站上的许多帖子后,我尝试以多种方式实现这一目标。 根据我的阅读,使用BackGroundWorker 是最好的方法。所以我复制了示例Microsoft Thread safe example,并在Form2 上有一个按钮以使用BackGroundWorker,以成功显示TextBox 中的文本。但是,当我尝试从 SerialPortClass 运行 BackGroundWorker 时,我得到一个:

抛出异常:SerialTest.exe 中的“System.NullReferenceException” 附加信息:对象引用未设置为对象的实例。

谁能指点我正确的方向?

我知道我实际上正在传递字符串,但只是尝试在另一个类中启动后台工作作为测试

完整的 SerialPort 类:

using System;
using System.IO;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.IO.Ports;
using System.Windows.Forms;
using System.Collections;
using System.Threading;
using System.Reflection;
using UsbLibrary;

namespace SerialTest

    public class SerialPortClass : Form
    

        private static SerialPortClass instance;

        private System.IO.Ports.SerialPort serialPort1 = new SerialPort();      // Initilises an instance of COM port
        private System.IO.Ports.SerialPort serialPort2 = new SerialPort();      // and another

        Form1 form1;   
        Form2 form2;

        internal delegate void SerialDataReceivedEventHandlerDelegate(
                 object sender, SerialDataReceivedEventArgs e);

        delegate void SetTextCallback(string text);
        string InputData = String.Empty;
        private static SerialPort port;

        public SerialPortClass()
        
            serialPort1.DataReceived += 
                  new System.IO.Ports.SerialDataReceivedEventHandler(port_DataReceived_1);
        

        public static readonly SerialPortClass _instance = new SerialPortClass();

        public ThreadStart ThreadProcSafe  get; private set; 

        public bool serialOpen(int port)
        
            if (port == 1)
            
                if (serialPort1.IsOpen) return true;
                else return false;
            
            else if (port == 2)
            
                if (serialPort2.IsOpen) return true;
                else return false;
            
            else return false;
        

        public void serialSendString(int port, string command)
        
            if (port == 1)
            
                // If the port is closed, don't try to send a character.
                if (!serialPort1.IsOpen) return;

                serialPort1.WriteLine(command);
            
            else if (port == 2)
            
                if (!serialPort2.IsOpen) return;

                serialPort2.WriteLine(command);
            
            else
            
                MessageBox.Show("Invalid port no");
            
        

        public void serialSendString1(string command)
        
            // If the port is closed, don't try to send a character.
            if (serialPort1.IsOpen)
            
                serialPort1.WriteLine(command);
            
            else
            
                MessageBox.Show("port not opening at connect..");
                return;
            
        

        public void serialSendString2(string command)
        
            // If the port is closed, don't try to send a character.
            if (serialPort2.IsOpen)
            
                serialPort2.WriteLine(command);
            
            else
            
                MessageBox.Show("port not opening at connect..");
                return;
            
        

        public void Connect()   //SerialTest.Form1 form)   //string comPortNo, int baud)
        
            serialPort1.PortName = "COM38"; // comPortNo;
            serialPort1.BaudRate = 9600;    // baud;

            if (serialOpen(1))
            
                MessageBox.Show("Serial port already open");
                return;
            
            try
            
                serialPort1.Open();
                serialPort1.NewLine = "\r";
            
            catch (Exception ex)
            
                MessageBox.Show(ex.ToString());
            
            if (serialOpen(1))
            
                Console.WriteLine("port open");
            
            else
            
                MessageBox.Show("port not opening at connect..");
            
        

        private void port_DataReceived_1(object sender, SerialDataReceivedEventArgs e)
        
            Console.WriteLine("Data recieved");
            InputData = serialPort1.ReadExisting();
            if (InputData != String.Empty)
            
                SetText(InputData);
            
        

        public void SetText(string text)
        
            Console.WriteLine("set text");
            form2.backgroundWorker1.RunWorkerAsync();
        

        public void disconnect()
        
            //serialPort1.PortName = "COM38";
            //serialPort1.BaudRate = 9600;

            if (serialOpen(1))
            
                serialPort1.Close();
                Console.WriteLine("Port closed");
            
            else
            
                MessageBox.Show("Port not open to close");
            
        

        public class SerialErrorReceivedEventArgs : EventArgs
        
            //Data to pass to the event
            public string LineData  get; private set; 

            public SerialErrorReceivedEventArgs(string lineData)
            
                this.LineData = lineData;
            
        
    

和Form2:

using System;
using System.Threading;
using UsbLibrary;
using log4net;
using SensorTestApp;
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.Windows.Forms;
using SerialTest;
using System.IO.Ports;


namespace SerialTest

    public partial class Form2 : Form
    
        string comPortNo;

        // This delegate enables asynchronous calls for setting
        // the text property on a TextBox control.
        delegate void SetTextCallback(string text);

        // This thread is used to demonstrate both thread-safe and
        // unsafe ways to call a Windows Forms control.
        public Thread demoThread = null;

        // This BackgroundWorker is used to demonstrate the 
        // preferred way of performing asynchronous operations.
        public BackgroundWorker backgroundWorker1;

        //private TextBox tBQuery;
        private Button setTextUnsafeBtn;
        private Button setTextSafeBtn;
        private Button setTextBackgroundWorkerBtn;

        private System.ComponentModel.IContainer components1 = null;

        public static  Form2 _instance = new Form2();


        public Form2()
        
            InitializeComponent();

            this.backgroundWorker1 = new BackgroundWorker();
            // here you have also to implement the necessary events
            // this event will define what the worker is actually supposed to do

            this.backgroundWorker1.DoWork += backgroundWorker1_DoWork;

            //this.backgroundWorker1.RunWorkerAsync += backgroundWorker1_RunWorkerAsync;

            // this event will define what the worker will do when finished
            this.backgroundWorker1.RunWorkerCompleted += backgroundWorker1_RunWorkerCompleted;

            btnRelay1On.Enabled = false;
            btnRelay2On.Enabled = false;
            btnRelay3On.Enabled = false;
            btnRelay4On.Enabled = false;
            btnRelay5On.Enabled = false;
            btnRelay1Off.Enabled = false;
            btnRelay2Off.Enabled = false;
            btnRelay3Off.Enabled = false;
            btnRelay4Off.Enabled = false;
            btnRelay5Off.Enabled = false;
        

        // This event handler creates a thread that calls a 
        // Windows Forms control in a thread-safe way.
        private void button2_Click(
            object sender,
            EventArgs e)
        
            this.demoThread =
                new Thread(new ThreadStart(this.ThreadProcSafe));

            this.demoThread.Start();
        

        // This method is executed on the worker thread and makes
        // a thread-safe call on the TextBox control.
        public void ThreadProcSafe()
        
            this.SetText("This text was set safely.");
        

        // This method demonstrates a pattern for making thread-safe
        // calls on a Windows Forms control. 
        //
        // If the calling thread is different from the thread that
        // created the TextBox control, this method creates a
        // SetTextCallback and calls itself asynchronously using the
        // Invoke method.
        //
        // If the calling thread is the same as the thread that created
        // the TextBox control, the Text property is set directly. 

        public void AppendText(String text)
        
            if (this.InvokeRequired)
            
                this.Invoke(new Action<string>(AppendText), new object[]  text );
                return;
            
            this.richTextBox1.Text += text;
        

        public void SetText(string text)
        
            // InvokeRequired required compares the thread ID of the
            // calling thread to the thread ID of the creating thread.
            // If these threads are different, it returns true.
            if (this.tBQuery.InvokeRequired)
            
                SetTextCallback d = new SetTextCallback(SetText);
                this.Invoke(d, new object[]  text );
                Console.WriteLine("different thread, text callback");
            
            else
            
                Console.WriteLine("same thread, string: %s", text);
                this.tBQuery.Text = text;
            
        

        // This event handler starts the form's 
        // BackgroundWorker by calling RunWorkerAsync.
        //
        // The Text property of the TextBox control is set
        // when the BackgroundWorker raises the RunWorkerCompleted
        // event.
        public void button1_Click(
            object sender,
            EventArgs e)
        
            this.backgroundWorker1.RunWorkerAsync();
        

        public void backgroundWorker1_DoWork(object sender,
            DoWorkEventArgs e)
        
            Console.WriteLine("BackgroundWorker1_Do Work");
        

        // This event handler sets the Text property of the TextBox
        // control. It is called on the thread that created the 
        // TextBox control, so the call is thread-safe.
        //
        // BackgroundWorker is the preferred way to perform asynchronous
        // operations.

        public void backgroundWorker1_RunWorkerCompleted(
            object sender,
            RunWorkerCompletedEventArgs e)
        
            this.tBQuery.Text =
                "This text was set safely by BackgroundWorker.";
        

        private void cbComPort_SelectedIndexChanged(object sender, EventArgs e)
        
            comPortNo = cbComPort.Text.ToString();
            btnOpenCom.Enabled = true;
        

        private void btnOpenCom_Click(object sender, EventArgs e)
        
            //SerialPortClass.GetInstance().Connect(comPortNo, 9600);

            try
            
                SerialPortClass._instance.Connect(); 
            
            catch (Exception ex)
            
                MessageBox.Show(ex.ToString());
            

            if (SerialPortClass._instance.serialOpen(1))
            
                btnOpenCom.Enabled = false;
                btnCloseCom.Enabled = true;
                btnRelay1On.Enabled = true;
                btnRelay2On.Enabled = true;
                btnRelay3On.Enabled = true;
                btnRelay4On.Enabled = true;
                btnRelay5On.Enabled = true;
                btnRelay1Off.Enabled = true;
                btnRelay2Off.Enabled = true;
                btnRelay3Off.Enabled = true;
                btnRelay4Off.Enabled = true;
                btnRelay5Off.Enabled = true;
            
            else MessageBox.Show("port not open btnOpenCom"); 
        

        private void btnCloseCom_Click(object sender, EventArgs e)
        
            try
            
                SerialPortClass._instance.disconnect();
            
            catch (Exception ex)
            
                MessageBox.Show(ex.ToString());
            
            if (!SerialPortClass._instance.serialOpen(1))
            
                btnOpenCom.Enabled = true;
                btnCloseCom.Enabled = false;
                btnRelay1On.Enabled = false;
                btnRelay2On.Enabled = false;
                btnRelay3On.Enabled = false;
                btnRelay4On.Enabled = false;
                btnRelay5On.Enabled = false;
                btnRelay1Off.Enabled = false;
                btnRelay2Off.Enabled = false;
                btnRelay3Off.Enabled = false;
                btnRelay4Off.Enabled = false;
                btnRelay5Off.Enabled = false;
            
        

        private void btnRelay1On_Click(object sender, EventArgs e)
        
            SerialPortClass._instance.serialSendString(1, "OH1");
        

        private void btnRelay1Off_Click(object sender, EventArgs e)
        
            SerialPortClass._instance.serialSendString(1, "OL1");
        

        private void btnRelay2On_Click(object sender, EventArgs e)
        
            SerialPortClass._instance.serialSendString(1, "OH2");
        

        private void btnRelay2Off_Click(object sender, EventArgs e)
        
            SerialPortClass._instance.serialSendString(1, "OL2");
        

        private void btnRelay3On_Click(object sender, EventArgs e)
        
            SerialPortClass._instance.serialSendString(1, "OH3");
        

        private void btnRelay3Off_Click(object sender, EventArgs e)
        
            SerialPortClass._instance.serialSendString(1, "OL3");
        

        private void btnRelay4On_Click(object sender, EventArgs e)
        
            SerialPortClass._instance.serialSendString(1, "OH4");
        

        private void btnRelay4Off_Click(object sender, EventArgs e)
        
            SerialPortClass._instance.serialSendString(1, "OL4");
        

        private void btnRelay5On_Click(object sender, EventArgs e)
        
            SerialPortClass._instance.serialSendString(1, "OH5");
        

        private void btnRelay5Off_Click(object sender, EventArgs e)
        
            SerialPortClass._instance.serialSendString(1, "OL5");
        

        private void btnQuery_Click(object sender, EventArgs e)
        
            //this.BeginInvoke(new SetTextCallback(SetText), new object[]  "hjdfdsfj" );
            SerialPortClass._instance.serialSendString(1, "?");
            Console.WriteLine("?");
        

    

Designer 文件中还有对 BackgroundWorker 的引用,所以我也将其包含在此处:

this.backgroundWorker1 = new System.ComponentModel.BackgroundWorker();

// 
// backgroundWorker1
// 
this.backgroundWorker1.RunWorkerCompleted += new       System.ComponentModel.RunWorkerCompletedEventHandler(this.backgroundWorker1_RunWorkerCompleted);

 //private System.ComponentModel.BackgroundWorker backgroundWorker1;

【问题讨论】:

你从哪里得到异常? 你在哪里初始化form2?你在哪里初始化backgroundWorker1null 是哪个对象? 你还没有为backgroundWorker1设置动作 后台工作人员实际上应该做什么?我没有看到任何与之相关的SerialPort 操作 您是否尝试过将 BackgroundWorker 组件添加到表单(从工具箱中选择)而不是声明变量。 【参考方案1】:

您缺少的是BackGroundWorker 的初始化。你应该在构造函数中这样做:

    public Form2()
    
        InitializeComponent();

        this.backgroundWorker1 = new BackGroundWorker();
        // here you have also to implement the necessary events
        // this event will define what the worker is actually supposed to do
        this.backgroundWorker1 .DoWork += backgroundWorker1r_DoWork;
        // this event will define what the worker will do when finished
        this.backgroundWorker1 .RunWorkerCompleted += backgroundWorker1_RunWorkerCompleted;

    

编辑:

当我更清楚地阅读您的帖子时。如果你想:

我尝试从 SerialPortClass 运行 BackGroundWorker

您必须确保在您的SerialPortClass 中有一个Form2 的实例。请注意,如果您只使用 new 关键字,它可能与您的显示器上已经显示的实例不同。

编辑 2:

好的,似乎出现了一种模式。如果我错了请纠正我 据我了解,您打开Form1,其中有一个字段SerialPortClass sp_class。在Form1你按下一个按钮,这个按钮调用方法:

sp_class.SetText();

现在你遇到了一个问题,因为显然当你来到这条线时:

form2.backgroundWorker1.RunWorkerAsync();

form2null 因为它从未被实例化过!请执行以下操作:创建一个实例并从 SerialPortClass 打开表单,如下所示:

form2 = new Form2();
form2.backgroundWorker1.RunWorkerAsync();
form2.Show();

现在,backgroundWorker1 的事件注册仍然属于 Form2 类,因为这是你真正让它运行的地方!实例还在Form2,请不要混在Form1Form2SerialPortClass这个三角形里

编辑 3:

由于您的SerialPort 触发了backgroundWorker1,您应该已经在SeralPortClass 的构造函数中创建Form2 的实例,如下所示:

public SerialPortClass()

    form2 = new Form2();

    serialPort1.DataReceived += 
          new System.IO.Ports.SerialDataReceivedEventHandler(port_DataReceived_1);

但你真正应该做的是解开你在你的 3 个似乎相互依赖的类之间创建的结,将 Form2 的实例传递给 SerialPortClass 的构造函数,如下所示:

public SerialPortClass(Form2 f2)

    form2 = f2

    serialPort1.DataReceived += 
          new System.IO.Ports.SerialDataReceivedEventHandler(port_DataReceived_1);

我猜你在Form1 中创建了一个你调用的Form2 和你用来调用SetTextSerialPortClass 的实例。正是在那里,您需要将实例传递给构造函数调用:

SerialPortClass my_sp_class = new SerialPortClass(form2); 

这将确保您在所需的表单上显示文本

【讨论】:

感谢Mong Zhu,我没有在构造函数中添加缺少的初始化,仍然抛出异常。我有一个 Form2 的实例:“Form2 form2;”请问在哪里使用新关键字? @chasher 你有 not 添加 [..] 并且仍然得到错误?所以你没有改变任何东西并且错误仍然存​​在?您如何将 form2 实例放入您的 SerialPortClass ?你能张贴整个班级吗?您能否也发布SerialPortClass.SetText() 的呼叫站点?这是了解您的问题的决定性因素。我猜form2form2.backgroundWorker1.RunWorkerAsync(); 中是null,对吗?调试器在单步执行时会告诉您什么? 抱歉,添加了“现在”。 form2 实例 'Form2 form2;'一两分钟后在“SerialPortClass”发布代码中谢谢! @chasher 我编辑了我的回复看看。您似乎对这个三角形内的数据如何传递感到困惑。重要的是此方法的调用:SerialPortClass.SetText() this you have not shown 。这发生在哪里?在Form1 ? 感谢您的帮助,我正在使用我从这里学到的知识并开始一个新的解决方案,使用一个 form1,带有一个打开 form2 的按钮并从那里逐渐建立【参考方案2】:

问题就在这里。

form2.backgroundWorker1.RunWorkerAsync();

您已经创建了一个backgroundwoker,但没有向它注册任何方法/委托。

通过在form2的构造函数中添加这个来分配它,

this.backgroundWorker1.DoWork += backgroundWorker1_DoWork; //not found in snippet
this.backgroundWorker1.RunWorkerCompleted +=backgroundWorker1_RunWorkerCompleted;

另外,正如Mong-Zhu所说,你需要在form2中像这样初始化Backgroundworker。

this.backgroundWorker1 = new BackGroundWorker();

另外,我个人认为调用不同形式的backgroundworker并不是一个好主意。

【讨论】:

感谢 Prajwal,我已经尝试了您和 Mong Zhu 的建议,但在调用 backgroundworker 时仍然出现异常,请问有什么更好的方法来实现我想要的? 有什么例外?一样吗? 我们都假设您在SerialPortClass 中引用了Form2 asform2。你有吗? @ Prajwal 是参考:'Form2 form2;'在“SerialPortClass”中 这不是对 Form2 旧实例的引用。您正在创建一个新的Form2,它与旧的Form2 实例无关。除非form2SerialPortClass 的孩子。【参考方案3】:

在阅读了 cmets 等之后,我可以看到您没有引用相同的 form2,并且您也没有初始化一个新的,这意味着所述空引用无效。

当你做代码时

Form2 form2;

这只是准备一个要分配的变量,由于您还没有这样做,因此您无法访问它的对象。如果您希望引用您的 form2,请在您可以访问它的地方对其进行全局初始化,然后您可以从那里使用它的对象。

如果您需要更多指导,请回复:)

【讨论】:

【参考方案4】:

您对 BackgroundWorker 的使用似乎不完整。您可以将它与更简单的设计器一起使用,也可以在代码隐藏中使用。

要与设计器一起使用,请将 backgroundWorker 对象拖放到组件托盘上。然后单击新创建的 BackgroundWorker1 对象。转到属性托盘并选择事件的闪电。有 3 个事件,您需要的两个是 DoWork 和 RunWorkerCompleted。双击两者为您生成方法。

在您的代码 sn-p 中显示您缺少 DoWork 部分,并且没有实际实例化您的 BackGroundWorker 对象。它的流动方式是这样的:BackgroundWorker 被声明和初始化。当您需要进行异步调用时,您会引发 .RunWorkerAsync 事件。该 RunWorkerAsync 事件将进入您的 DoWork 事件处理程序。这是您放置您希望异步执行的工作的代码的地方。一旦此 DoWork 事件到期,就会调用 RunWorkercompleted 事件。一旦这个 RunWorkerCompleted 事件过期,新线程也会过期。

如果您尝试在与创建它的线程不同的线程中更改设计器组件(即,从 BackgroundWorker 的 DoWork 事件处理程序中设置按钮文本),您必须确保使用 .InvokeRequired,如看来你已经开始了。

如果您从 form2 外部调用此 backgroundworker,请确保您将 form2 的句柄提供给该类。从 sn-p 中不清楚您是否实例化了一个新的。一个简单的方法是在序列化类中为 Form2 创建一个私有字段,以及一个方法,比如 AttachForm(Form f),然后从 Form2 调用该方法,将 this 作为参数传递。

【讨论】:

谢谢,我明白了,

以上是关于c#文本框,不同类,backgroundWorker的主要内容,如果未能解决你的问题,请参考以下文章

(转)C# 使用BackgroundWorker

(C#) BackgroundWorker() ProgressChanged 不工作

C# BackGroundWorker实现窗体不卡死 进度条功能

C# BackgroundWorker 详解

C# BackgroundWorker 详解

C# BackgroundWorker 详解