使用非主 UI 线程显示表单消息

本文关键字:显示 表单 消息 线程 UI | 更新日期: 2023-09-27 18:36:11

我有一个项目,它没有 usign 任何表单/按钮或类似的东西,与 Websocket 连接并使用异步方法接收一些消息(在我自己创建的表单上)应该出现在屏幕的右上角。

但是,如果 websocket 没有说它必须停止,则此消息可能会不时(2 或 3 分钟)出现在屏幕上。这个消息可以足够大,为了让它看起来更好,我让我的信息以不止一种形式出现。

它给人的印象是通知。因此,我的类与 websocket 连接并接收消息异步,使用作为控制器的线程调用另一个类。控制器的目的是不时地在各种新的 form() 通知中显示该消息,如果 websocket 不返回任何消息,显然不要这样做。

但是当我调用form.show时,程序停止工作。我已经环顾了stackoverflow,但我发现的想法似乎行不通。

有人说我应该使用调用,但它一直给出错误说"在创建窗口句柄之前,无法在控件上调用 Invoke 或 BeginInvoke",尝试像这样解决:C# 从另一个线程调用 form.show(),但它不起作用。

有人说我应该使用 .showDialog 而不是 .show,但它似乎并不好,因为它等待窗口关闭以终止该方法,并且正如我所说,我需要同时打开多个通知。

有人说该表单是用 .show 打开的,但它开放的时间很短。但我无法注意到是不是这种情况,即使是这样,我也无法解决它。好吧,重要的是我被困住了,我不知道该怎么做。

用代码编辑:

//Main
Application.Run(new SocketService());
//SocketService class
        public SocketService()
        {
            alerta = null;
            while (true)
            {
                try
                {
                    //Console.WriteLine("Nome do Usúario:" + Environment.UserName);
                    Thread.Sleep(2000);
                    Connect("ws://192.168.120.38:9091").Wait();
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex);
                }
            }
        }
        public static async Task Connect(string uri)
        {
            ClientWebSocket webSocket = null;
            try
            {
                webSocket = new ClientWebSocket();
                await webSocket.ConnectAsync(new Uri(uri), CancellationToken.None);
                await Login(webSocket);
            }
            catch (Exception ex)
            {
                throw ex;
            }
            finally
            {
                if (webSocket != null)
                    webSocket.Dispose();
                lock (consoleLock)
                {
                    Console.ForegroundColor = ConsoleColor.Red;
                    Console.WriteLine("WebSocket closed.");
                    Console.ResetColor();
                }
            }
        }
        private static async Task Login(ClientWebSocket webSocket)
        {
            ArraySegment<Byte> buffer = new ArraySegment<byte>(encoder.GetBytes(        "{'"event'":'"loginBrowser'",'"data'":{'"login'":'"000000003077'",'"data'":'"1'"}}"));
            await webSocket.SendAsync(buffer, WebSocketMessageType.Text, true, CancellationToken.None);
            if (webSocket.State == WebSocketState.Open)
            {
                if (ShowMessage.created != true)
                {
                    var dummy = new Control(); // to initialize SynchronizationContext
                    _sync = SynchronizationContext.Current;
                    new Thread(ThreadProc).Start();
                }
                await Receive(webSocket);               
            }
        }
        private static async Task Receive(ClientWebSocket webSocket)
        {
            while (webSocket.State == WebSocketState.Open)
            {
                ArraySegment<Byte> buffer = new ArraySegment<byte>(new Byte[256]);
                var result = await webSocket.ReceiveAsync(buffer, CancellationToken.None);
                if (result.MessageType == WebSocketMessageType.Close)
                {
                    await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty,        CancellationToken.None);
                }
                else
                {
                    if (result.EndOfMessage)
                    {
                        message += encoder.GetString(buffer.ToArray());
                        SendMessage(message);
                    }
                    else
                    {
                        message += encoder.GetString(buffer.ToArray());
                    }
                }
            }
        }
        public static void ShowFormFromAnotherThread(string text)
        {
            _sync.Post(SendOrPostCallback, text);
        }
        private static void SendOrPostCallback(object state)
        {
            var form = new Notification();
            form.Text = (string)state;
            form.Show();
        }
        private static void ThreadProc()
        {
            while (true)
            {
                Thread.Sleep(2000); // wait imitation
                ShowFormFromAnotherThread("HI");
            }
        }
         /*Notification is my form and depending on where I put this part:
         var dummy = new Control(); // to initialize SynchronizationContext
        _sync = SynchronizationContext.Current;
        new Thread(ThreadProc).Start();

或者我不调用登录或不输入 receive() 方法或最佳情况 它接收信息 调用 threadProc 和 ShowFormFromAnotherThread,但不输入 SednOrPostCallBack*/

使用非主 UI 线程显示表单消息

using System.Threading;
using System.Windows.Forms;
namespace ConsoleThreadSync
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            Application.Run(new App());
        }
    }
    public class App : ApplicationContext
    {
        private readonly SynchronizationContext _sync;
        public App()
        {
            var dummy = new Control(); // to initialize SynchronizationContext
            _sync = SynchronizationContext.Current;
            new Thread(ThreadProc).Start();
        }
        public void ShowFormFromAnotherThread(string text)
        {
            _sync.Post(SendOrPostCallback, text);
        }
        private void SendOrPostCallback(object state)
        {
            var form = new Form1();
            form.Text = (string)state;
            form.Show();
        }
        private void ThreadProc()
        {
            while (true)
            {
                Thread.Sleep(2000); // wait imitation
                ShowFormFromAnotherThread("HI");
            }
        }
    }
}

尝试调用它:

var dummy = new Control(); // to initialize SynchronizationContext
_sync = SynchronizationContext.Current;

来自构造器 SocketService() 而不是来自异步方法。这是一个初始化代码,它必须从主线程调用

好的,在阅读了更多内容之后,该解决方案是这个,但是使用的唯一方法.show 来自通知者是使用 Application.DoEvents 并且我已经从我调查过的来源中得到了警告除非是唯一的选择,否则不应使用此方法,因为它可能会导致线程和其他内容出现一些问题。因此,除非有人可以给我另一个提示或线索,否则我有两种选择或使用此方法并尝试修复其他一些错误它可能导致或使用 .showDialog,因为不知道为什么它可以正常工作而没有任何其他问题,但要使用 .showDialog,我已经使用我创建和显示通知的另一个线程,因为如果我不这样做,循环将在每次迭代时停止为了等待 .showDialog 被关闭。由于这不是问题,我想避免使用大量线程,因为它可能会导致它们之间的同步的另一个问题:

namespace ReiDoCSharp
{
    class ShowMessage
    {
        private static RootObject alerta;
        public static bool created;
        private static int startPosition;
        public static void setStartPosition(int start)
        {
            if (start < startPosition)
            {
                startPosition = start;
            }
        }
        public RootObject getAlerta()
        {
            return ShowMessage.alerta;
        }
        public void setAlerta(RootObject root)
        {
            ShowMessage.alerta = root;
        }
        private static void DoWork()
        {
            while (true)
            {
                if (created != true)
                {
                    created = true;
                }
                if (alerta != null)
                {
                    string mensagem = "";
                    if ((alerta.data.Informacoes[1] != "") && (alerta.data.Informacoes[1] != null))
                    {
                        mensagem += alerta.data.Informacoes[1];
                    }
                    if ((alerta.data.Informacoes[0] != "") && (alerta.data.Informacoes[0] != null))
                    {
                        mensagem += alerta.data.Informacoes[0];
                    }
                    if (mensagem != "")
                    {
                        startPosition = 5;

                        string[] messages = mensagem.Split(new[] { "<br><br>" }, StringSplitOptions.None);
                        foreach (string message in messages)
                        {
                            Notification popup = new Notification();
                            popup.label1.Text = message;
                            popup.TopMost = true;
                            popup.Show();
                            Application.DoEvents();
                            /*Solution with the ShowDialog would be:
                                Task.Run(() => showNotification(message));
                            */
                        }
                    }
                }
                Thread.Sleep(5000);
            }
        }
        //Then I won't need to use Application.DoEvents, but would have to create more threads
        private static Task showNotification(string message)
        {
            Notification popup = new Notification();
            popup.label1.Text = message;
            popup.TopMost = true;
            popup.ShowDialog();
        }
        public static Task createPopupsAsync()
        {
            Task.Run(() => DoWork());
        }
    }
}
namespace ReiDoCSharp
{
    class SocketService
    {
        private static object consoleLock = new object();
        private const bool verbose = true;
        private static readonly TimeSpan delay = TimeSpan.FromMilliseconds(3000);
        private static UTF8Encoding encoder = new UTF8Encoding();
        private static string message;
        private static RootObject alerta;
        public SocketService()
        {
            Begin();
        }
        public static void Begin()
        {
            alerta = null;
            while (true)
            {
                try
                {
                    Thread.Sleep(2000);
                    Connect("ws://192.168.120.38:9091").Wait();
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex);
                }
            }
        }
        public static async Task Connect(string uri)
        {
            ClientWebSocket webSocket = null;
            try
            {
                webSocket = new ClientWebSocket();
                await webSocket.ConnectAsync(new Uri(uri), CancellationToken.None);
                await Login(webSocket);
            }
            catch (Exception ex)
            {
                throw ex;
            }
            finally
            {
                if (webSocket != null)
                    webSocket.Dispose();
                lock (consoleLock)
                {
                    Console.ForegroundColor = ConsoleColor.Red;
                    Console.WriteLine("WebSocket closed.");
                    Console.ResetColor();
                }
            }
        }
        private static async Task Login(ClientWebSocket webSocket)
        {
            ArraySegment<Byte> buffer = new ArraySegment<byte>(encoder.GetBytes("{'"event'":'"loginBrowser'",'"data'":{'"OPERADOR'":'"000000003077'",'"NRORG'":'"1'"}}"));
            await webSocket.SendAsync(buffer, WebSocketMessageType.Text, true, CancellationToken.None);
            if (webSocket.State == WebSocketState.Open)
            {
                Task.Factory.StartNew(() => ShowMessage.createPopupsAsync());
                await Receive(webSocket);
            }
        }
        private static async Task Receive(ClientWebSocket webSocket)
        {
            while (webSocket.State == WebSocketState.Open)
            {
                ArraySegment<Byte> buffer = new ArraySegment<byte>(new Byte[256]);
                var result = await webSocket.ReceiveAsync(buffer, CancellationToken.None);
                if (result.MessageType == WebSocketMessageType.Close)
                {
                    await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None);
                }
                else
                {
                    if (result.EndOfMessage)
                    {
                        message += encoder.GetString(buffer.ToArray());
                        SendMessage(message);
                    }
                    else
                    {
                        message += encoder.GetString(buffer.ToArray());
                    }
                }
            }
        }
        private static void LogStatus(bool receiving, byte[] buffer, int length, string assunto)
        {
            lock (consoleLock)
            {
                Console.ForegroundColor = receiving ? ConsoleColor.Green : ConsoleColor.Yellow;
                if (verbose)
                {
                    Console.WriteLine(encoder.GetString(buffer) + "  " + assunto);
                }
                Console.ResetColor();
            }
        }
        private static void SendMessage(string message)
        {
            message = message.Replace("event", "evento");
            message = message.Replace("'0", "");
            JavaScriptSerializer js = new JavaScriptSerializer();
            RootObject mess = js.Deserialize<RootObject>(message);
            if (mess.data.Informacoes[1] != "")
            {
                mess.data.Informacoes[1] += "<br>";
            }
            if (alerta == null)
            {
                alerta = mess;
            }
            else
            {
                if ((mess.data.Quantidade[0] != 0) && (mess.data.Quantidade == null))
                {
                    if ((mess.data.Quantidade[0] == -1) && (mess.data.Informacoes[0] == ""))
                    {
                        alerta = null;
                    }
                    else
                    {
                        alerta = mess;
                    }
                }
                else if (mess.data.Quantidade[0] == 0)
                {
                    alerta = null;
                }
                if ((mess.data.Quantidade[1] != 0) && (mess.data.Informacoes[1] != ""))
                {
                    alerta = mess;
                }
            }
            new ShowMessage().setAlerta(alerta);
            message = "";
        }
    }
}