代码之家  ›  专栏  ›  技术社区  ›  Jam

更新GUI时冻结

  •  0
  • Jam  · 技术社区  · 7 年前

    我有一个带有标题、标签和OK按钮的警报框。每当它弹出时,它应该重复使用同一个窗口,而不是打开一个新窗口。

    我从外部线程接收到一个对话框事件。它可以接收已知过程之外的消息。我想打电话 Show() 每次信息到达时。

    enter image description here

    public partial class AlertBox : Form
    {
        private static AlertBox instance;
        public static AlertBox Instance => instance ?? (instance = new AlertBox());
    
        private AlertBox()
        {
            InitializeComponent();
        }
    
        private void AlertBox_Load(object sender, EventArgs e)
        {
            MessageLabel.Text = "";
            Text = "";
        }
    
        public void Show(string text, string title)
        {
            Show();
            BringToFront();
            Text = title;
            MessageLabel.Text = text;
        }
    
        private void OkButton_Click(object sender, EventArgs e)
        {
            MessageLabel.Text = "";
            Text = "";
            Hide();
        }
    }
    

    但是打电话的时候, Show(text, title) 如图所示:

    internal class Driver
    {
        private readonly AlertBox _alertBox = AlertBox.Instance;
        public void Initialize()
        {
            // Receive dialog event.
            _connection.OnDialogReceived += (text, title) =>
            {
                _alertBox.Show(text, title);
            };
        }
    }
    

    此时将显示警报框,设置窗口标题并冻结。请注意,按钮已不可见。

    enter image description here

    我试着用 Invoke . 它冻结的结果完全相同。

        public void ShowWithInvoke(string text, string title)
        {
            if (!Created)
            {
                CreateControl();
            }
    
            if (!IsHandleCreated)
            {
                CreateHandle();
            }
    
            Invoke((MethodInvoker)delegate
            {
                Show();
                BringToFront();
                Text = title;
            });
    
            if (!MessageLabel.Created || !MessageLabel.IsHandleCreated)
            {
                MessageLabel.CreateControl();
            }
    
            MessageLabel.Invoke((MethodInvoker)delegate
            {
                MessageLabel.Text = text;
            });
        }
    
    1 回复  |  直到 7 年前
        1
  •  1
  •   Enigmativity    7 年前

    解决此问题的常用方法是将发送警报的代码与显示警报的代码解耦。

    首先,您应该创建一个类,该类的唯一目的是将更新传递给正在侦听的任何UI。

    public class MessageUpdater
    {
        public event EventHandler<string> MessageSent;
        public void SendMessage(string message)
        {
            this.MessageSent?.Invoke(this, message);
        }
    }
    

    AlertBox 你只要接受一个 MessageUpdater 实例并更新 Label 每当 MessageSent 事件被激发。

    public partial class AlertBox : Form
    {
        public AlertBox()
        {
            InitializeComponent();
        }
    
        private MessageUpdater _messageUpdater = null;
    
        public MessageUpdater MessageUpdater
        {
            set
            {
                if (_messageUpdater != null)
                {
                    _messageUpdater.MessageSent -= UpdateMessage;
                }
                if (value != null)
                {
                    _messageUpdater = value;
                    _messageUpdater.MessageSent += UpdateMessage;
                }
            }
        }
    
        private void UpdateMessage(object sender, string message)
        {
            if (this.InvokeRequired)
            {
                this.Invoke((Action)(() => this.UpdateMessage(sender, message)));
            }
            else
            {
                this.MessageLabel.Text = message;
            }
        }
    }
    

    消息更新程序 (并删除现有的一个),然后在需要时将调用编组到UI线程。

            var mu = new MessageUpdater();
    
            var counter = 0;
            var timer = new System.Threading.Timer((System.Threading.TimerCallback)(x =>
            {
                mu.SendMessage((counter++).ToString());
            }), null, TimeSpan.FromSeconds(1.0), TimeSpan.FromSeconds(1.0));
    
            var ab = new AlertBox();
            ab.MessageUpdater = mu;
            ab.ShowDialog();
    

    这里棘手的部分是 System.Threading.Timer 我过去常常把信息推送到 消息更新程序 所以 .ShowDialog 不会冻僵的。

    推荐文章