我手工制作了一个我想要自动玩的游戏的 MVC 风格实现。我所说的“自动播放”是指用户在播放时通常会单击的按钮,我希望 Controller 自动启动。这样我就可以出于质量控制的原因观看游戏本身。这个特定的游戏有很多代码,因此我没有将其作为示例提供,而是使用相同的方法创建了一个愚蠢的 HelloWorld 示例。
在提供示例之前,这是我的问题:您在下面看到的所有内容都是功能性的,并且“有效”;除了一件事:我无法关闭自动播放,因为 UI 变得无响应,并且关闭它的按钮不会响应单击事件。
首先在解决方案中创建一个.Net 4.6.1 winforms项目。 (.net 版本可能并不重要,只要 >= 4.5 即可)。创建一个如下所示的表单:
在后面的代码中,复制粘贴以下内容:(根据编译需要更改名称)
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/