如何使用后台工作者将 class 中的信息发送回主窗体

How to send information from a class using a backgroundworker, back to the mainform

嗨,

我正在尝试编写一个侦听端口的 UDP 客户端,然后在文本框中显示传入的数据。

我写了一个class; UDP_Receive() 启动一个监听指定端口并接收数据的后台工作程序。该代码基于此问题; C# .Net receiving UDp packets in separater thread and application exit

我想我已经按照这个问题中接受的答案解决了阻塞 .receive() 的问题;

我的问题是如何将在 backgroundworkerthread(我在 class 中创建)中接收到的数据返回到主线程,以便我可以将其显示在文本框中?

我考虑过使用 Invoke,但我的 class 中没有对文本框的任何引用。

textBox_UdpPositionInData.Invoke(new EventHandler(delegate
{
   textBox_UdpPositionInData.Text = receivedData;
}));

我发现了一个关于 tcp 服务器的非常相似的问题,但我看不到如何在此处应用该解决方案 Send data from a background thread to the main thread

任何想法或建议当然非常感谢!

非常感谢!

    internal class UDP_Receive
    {
        private int _portToListen = 2003;
        private volatile bool listening;
        BackgroundWorker _backgroundWorker;

        //Constructor
        public UDP_Receive()
        {
            Debug.WriteLine("Constructor: UDP_Receive");
            this.listening = false;
        }

        public void StartListener() 
        {
            if ( (_backgroundWorker==null) || (!_backgroundWorker.IsBusy))
            {
                _backgroundWorker = new BackgroundWorker();
                _backgroundWorker.DoWork += listenForUDPPackages_DoWork;
                _backgroundWorker.RunWorkerCompleted +=listenForUDPPackages_RunWorkerCompleted;
                _backgroundWorker.WorkerSupportsCancellation = true;
                _backgroundWorker.RunWorkerAsync();
                
                Debug.WriteLine("Creates a new thread: " + _backgroundWorker.ToString() );
                
                // We are listening
                this.listening = true;                
            }
        }

        public void StopListener()
        {
            // The user cancelled the UDP Port listening
            this.listening = false;

            // Cancel the backgroundworker
            _backgroundWorker.CancelAsync();

            // Debug
            Debug.WriteLine("Stops current thread: " + _backgroundWorker.ToString());
        
        }

        public bool IsListening
        {
            get { return this.listening; }            
        }

        public int PortToListen
        {
            get { return this._portToListen; }
            set { this._portToListen = value; }
        }


        private void listenForUDPPackages_DoWork(object sender, DoWorkEventArgs ev)        
        {
            
            UdpClient? listener = null;

            try
            {
                listener = new UdpClient(_portToListen);
            }
            catch (SocketException)
            {
                //do nothing
            }

            if (listener != null)
            {
                IPEndPoint groupEP = new IPEndPoint(IPAddress.Any, _portToListen);

                try
                {
                    while (this.listening)
                    {

                        Debug.WriteLine("Waiting for UDP broadcast to port " + _portToListen);
                        byte[] receivedBytes = new byte[1024];
                        string receivedData;
                        
                        bool timeTracker = TrackFunction(TimeSpan.FromSeconds(2), () =>
                        {
                            receivedBytes = listener.Receive(ref groupEP);
                        });


                        Debug.WriteLine("Timetracker result: " + timeTracker.ToString());

                        if (receivedBytes == null || receivedBytes.Length == 0)
                        {
                            // We did not recieve any data
                            Debug.WriteLine("No data received befor Time out ");
                        }
                        else
                        {
                            // We managed to receive some data!
                            // No we want to process the data and then send the result to the Thread that initiated the class.
                            receivedData = Encoding.Default.GetString(receivedBytes);
                            Debug.WriteLine("Data received: " + receivedData);
                        }
                    }
                catch (Exception e)
                {
                    Debug.WriteLine("Exception: " + e.ToString());
                }
                finally
                {
                    listener.Close();
                    Debug.WriteLine("Finally: Done listening for UDP broadcast");

                }
            }
            else
            {
                Debug.WriteLine("Error: UdpClient(_portToListen) returned null");
            }
        }

        private void listenForUDPPackages_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e == null || e.Result == null)
            {
                Debug.WriteLine("listenForUDPPackages_RunWorkerCompleted e = null");
            }
            else
            {

                if (e.Cancelled)
                {
                    Debug.WriteLine("Operation was canceled");
                }
                else if (e.Error != null)
                {
                    Debug.WriteLine("Error: " + e.Error.Message);
                }
                else
                {
                    Debug.WriteLine("Result: " + e.Result.ToString());
                }
            }
        }

        private static bool TrackFunction(TimeSpan timeSpan, Action codeBlock)
        {
            try
            {
                Task task = Task.Factory.StartNew(() => codeBlock());
                task.Wait(timeSpan);
                return task.IsCompleted;
            }
            catch (AggregateException ae)
            {
                throw ae.InnerExceptions[0];
            }
        }

    }

您可以编写这种代码来将 UI 与 UDP_Receive 分开 class:

internal class UDP_Receive
{
    private Action<string> _invoke;
    public UDP_Receive(Action<string> invoke)
    {
        _invoke = invoke;
    }
    
    public void Foo()
    {
        _invoke("Hello");
    }
}

当您声明 class 时,您可以这样做:

var ur = new UDP_Receive(t => textBox.Invoke(() => textBox.Text = t));

现在只需调用 ur.Foo()(在我的示例中)来更新 UI。