c# - 为什么在以编程方式控制时 UI 没有响应?

标签 c# winforms

我手工制作了一个我想要自动玩的游戏的 MVC 风格实现。我所说的“自动播放”是指用户在播放时通常会单击的按钮,我希望 Controller 自动启动。这样我就可以出于质量控制的原因观看游戏本身。这个特定的游戏有很多代码,因此我没有将其作为示例提供,而是使用相同的方法创建了一个愚蠢的 HelloWorld 示例。

在提供示例之前,这是我的问题:您在下面看到的所有内容都是功能性的,并且“有效”;除了一件事:我无法关闭自动播放,因为 UI 变得无响应,并且关闭它的按钮不会响应单击事件。

首先在解决方案中创建一个.Net 4.6.1 winforms项目。 (.net 版本可能并不重要,只要 >= 4.5 即可)。创建一个如下所示的表单:

enter image description here

在后面的代码中,复制粘贴以下内容:(根据编译需要更改名称)

using System;
using System.Threading;
using System.Windows.Forms;

namespace WinformsExample
{
    public partial class HelloWorldView : Form
    {
        private readonly HelloWorldController MyHelloWorldController;

        public HelloWorldView()
        {
            InitializeComponent();

            MyHelloWorldController = new HelloWorldController();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            MyHelloWorldController.HelloWorldRequested();

            if (MyHelloWorldController.IsAutomated)
            {
                Thread.Sleep(2000);
                button1.PerformClick();
            }
        }

        private void HelloWorldView_Load(object sender, EventArgs e)
        {
            MyHelloWorldController.HelloWorldRequestedEvent += OnHelloWorldRequested;
        }

        private void OnHelloWorldRequested(HelloWorldParameters parameters)
        {
            textBox1.Text += parameters.HelloWorldString + Environment.NewLine;
            textBox1.Update();
        }

        private void button2_Click(object sender, EventArgs e)
        {
            MyHelloWorldController.IsAutomated = !MyHelloWorldController.IsAutomated;

            if (MyHelloWorldController.IsAutomated)
            {
                button2.Text = "hello world - is on";
                button2.Update();
                button1.PerformClick();
            }
            else
            {
                button2.Text = "hello world - is off";
                button2.Update();
            }
        }
    }
}

并创建一个名为 HelloWorldController.cs 的类并将其复制粘贴到其中:

namespace WinformsExample
{
    public class HelloWorldParameters
    {
        public string HelloWorldString { get; set; }
    }

    public delegate void HelloWorldEventHandler(HelloWorldParameters parameters);

    public class HelloWorldController
    {
        private readonly HelloWorldParameters _parameters;

        public event HelloWorldEventHandler HelloWorldRequestedEvent;

        public bool IsAutomated { get; set; }

        public HelloWorldController()
        {
            _parameters = new HelloWorldParameters();
        }

        public void HelloWorldRequested()
        {
            _parameters.HelloWorldString = "Hello world!!";

                        if (HelloWorldRequestedEvent != null)
            HelloWorldRequestedEvent(_parameters);
        }
    }
}

...如果需要,请继续重命名。现在构建程序。单击第一个按钮。你会看到“ Hello World ”。现在单击第二个按钮,您将看到每 2 秒打印一次“hello world”。

认为的工作方式是,通过再次单击button2,它将停止自动播放。但是,UI 没有响应,并且按钮单击事件永远不会发生。

这里发生了什么导致 UI 无响应,我该如何修复它以获得预期的行为?

*更新 - 这是解决方案*

除了 HelloWorldView.cs 之外,保持所有内容与上面相同。删除对 Thread.Sleep() 的调用。将计时器从工具箱拖放到设计图面上。您将在设计器表面的底部看到一个标记为

的图标

timer1

将以下代码复制粘贴到 HelloWorldView.cs 中。编译并执行。如果一切正确,您应该能够随时通过单击按钮来打开和关闭“hello world”显示 - UI 保持响应。

using System;
using System.Windows.Forms;

namespace WinformsExample
{
    public partial class HelloWorldView : Form
    {
        private readonly HelloWorldController MyHelloWorldController;

        public HelloWorldView()
        {
            InitializeComponent();

            MyHelloWorldController = new HelloWorldController();
        }

        private void onTimerTick(object sender, EventArgs e)
        {
            button1.PerformClick();
        }

        private void OnHelloWorldRequested(HelloWorldParameters parameters)
        {
            textBox1.Text += parameters.HelloWorldString + Environment.NewLine;
            textBox1.Update();
        }

        private void HelloWorldView_Load(object sender, EventArgs e)
        {
            MyHelloWorldController.HelloWorldRequestedEvent += OnHelloWorldRequested;
        }

        private void button1_Click(object sender, EventArgs e)
        {
            MyHelloWorldController.HelloWorldRequested();
        }

        private void button2_Click(object sender, EventArgs e)
        {
            MyHelloWorldController.IsAutomated = !MyHelloWorldController.IsAutomated;

            if (MyHelloWorldController.IsAutomated)
            {
                button2.Text = "hello world - is on";
                button2.Update();

                timer1.Interval = 2000;
                timer1.Tick += onTimerTick;
                timer1.Start();
            }
            else
            {
                timer1.Stop();
                button2.Text = "hello world - is off";
                button2.Update();
            }
        }
    }
}

最佳答案

WinForms 使用单个消息泵线程(称为 UI 线程)。 (如果您不熟悉这个概念,您应该研究 Windows 消息和 Windows 消息泵)。

Thread.Sleep 导致当前正在执行的线程休眠或暂停一段时间。这种 sleep /暂停就像线程的死亡 - 它什么也意识不到,也无法做任何事情。

由于 WinForms 应用程序中当前执行的线程通常是 UI 线程 - Thread.Sleep 将导致 UI 变得无响应,因为它不再能够发送消息。

另一种设计是使用基于表单的计时器。将您的游戏代码放入计时器的 Tick 事件中。

关于c# - 为什么在以编程方式控制时 UI 没有响应?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35188931/

相关文章:

c# - 如何对以下列表进行排序

C# ActiveUp Mail 无法获取邮件

c# - 如何让 nanomsg 可靠地自动重新连接?

c# - 将正文添加到与 azure 服务 mgmt api 一起使用的 HttpWebRequest

c# - 有没有办法防止 Visual Studio 2008 Designer 在代码和设计窗口之间跳转时始终重绘窗口?

c# - 系统绘图反转区域构建路径

c# - 日期时间如何工作以及如何在 Windows 窗体 ADO 实体中比较两个日期?

c# - 带有 “Yes” 的消息框,C# 中的 “No” 选择?

c# - 使用 Windows Azure 自定义字体

c# - 与 ToolStrip 和 Panel 对接