提问人:Brendan Lynn 提问时间:11/11/2023 最后编辑:NimanthaBrendan Lynn 更新时间:11/12/2023 访问量:121
与程序同时运行表单
Run Form simultaneously with Program
问:
我有一个班级,试图创造一个更好的班级替代品。它的开始如下:System.Console
public class SuperConsole
{
private readonly Form _Form;
public SuperConsole()
{
_Form = new()
{
Text = DefaultTitle,
BackColor = DefaultBackColor,
};
Application.Run(_Form);
}
public string Title
{
get => _Form.Invoke(() => _Form.Text);
set => _Form.Invoke(() => _Form.Text = value);
}
//more stuff
}
显然,如果我运行构造函数,它会冻结在 .当然,窗体会运行,但构造函数的调用方必须稍等片刻。因此,我更改了构造函数以在新线程上运行:Application.Run
Application.Run
public SuperConsole()
{
_Form = new()
{
Text = DefaultTitle,
BackColor = DefaultBackColor,
};
Thread t = new(() => Application.Run(_Form));
t.Start();
}
但这会抛出一个.System.InvalidOperationException
System.InvalidOperationException:“在创建窗口句柄之前,无法对控件调用 Invoke 或 BeginInvoke。”
我不知道如何进行。我尝试了几种变体,但没有效果。
我希望表单和调用函数能够同时运行,并且仍然能够正常使用。我还希望让类的用户不必担心任何这些。我该怎么做?Form.Invoke
答:
我认为您必须在专用线程上创建,而不是在当前线程上创建,因为 UI 组件是线程仿射的。您还必须将线程声明为 STA,并等待表单创建完成。像这样的东西应该可以工作:Form
SuperConsole
SuperConsole
public class SuperConsole
{
private Form _form;
public SuperConsole()
{
ManualResetEventSlim mres = new();
Thread t = new(() =>
{
_form = new()
{
Text = DefaultTitle,
BackColor = DefaultBackColor,
};
mres.Set();
Application.Run(_form);
});
t.Name = "SuperConsole";
t.SetApartmentState(ApartmentState.STA);
t.Start();
mres.Wait();
}
public string Title
{
get => _form.Invoke(() => _Form.Text);
set => _form.Invoke(() => _Form.Text = value);
}
}
ManualResetEventSlim
用于指示实例已创建并分配给字段。否则,当前线程可以观察到 to be .Form
_form
_form
null
我还没有测试上面的代码。在消息循环开始之前,表单可能尚未准备好调用。在这种情况下,您可能需要在窗体的 HandleCreated
或 or 事件中移动。若要了解如何仅订阅一个通知的事件,请参阅此答案。Invoke
mres.Set();
Load
Shown
我找到了以下解决方案,类似于用户@TheodorZoulias的假设。
public SuperConsole()
{
_Form = new()
{
Text = DefaultTitle,
BackColor = DefaultBackColor,
};
ManualResetEventSlim mres = new();
_Form.HandleCreated += HandleCreated;
Thread t = new(() => Application.Run(_Form));
t.Start();
mres.Wait();
void HandleCreated(object? Sender, EventArgs E)
{
mres.Set();
_Form.HandleCreated -= HandleCreated;
}
}
特别感谢用户@TheodorZoulias指出为什么我的最后一次尝试不是最佳的。我借用了他使用班级和活动的想法。ManualResetEventSlim
Form.HandleCreated
但是,窗体不需要在与调用函数的同一线程上构造。Application.Run
评论
_Form
readonly
评论
readonly
readonly
Application.Run
Thread.Join