我有一个正在处理的项目,该项目具有Windows gui(可选),并且如果没有提供gui,则可以写入gui或控制台的工作程序。gui是可选的,以使该项目与可能没有桌面环境的系统向后兼容(我最终可能最终还是用C或C ++来重新构建该项目,但由于时间限制,我现在需要一些工作)。程序将运行在大多数计算机上(目前)具有Windows XP。(我的目标是.NET Framework 4.0.3)。
由于我希望gui是可选的,因此我不希望worker类位于aBackgroundWorker
或a内部Form
。在我的真实项目中,我有一个UserInterface
“接口”(c#接口),可以通过各种用户界面来实现。
在Windows GUI中,tfhere是Form
带有按钮的主按钮,可打开对话框Form
。该对话框有一个多行文本框,工作人员可以在其中附加行。
由于我没有使用aBackgroundWorker
或其他常规的处理方式,BeginInvoke
因此在创建窗口句柄之前遇到了与交叉线程操作和调用有关的各种问题。通过本质上调用_ = MainForm.Handle
inMainForm
的构造函数以强制在显示窗口之前创建窗口句柄,我能够“解决”窗口句柄问题(以便工作人员可以将行附加到文本框,这可能会在gui之前发生)已经显示过)。
这是我的最小的,可复制的示例,它捕获了我在实际项目中遇到的问题。当以下情况遇到此问题:a)从MainForm
构造函数中删除窗口句柄的创建,这会导致BeginInvoke
抱怨在创建窗口句柄之前被调用,或者b)像现在这样,一旦对话框窗口关闭并重新打开对它的调用ShowDialog
由于跨线程操作而失败。
Program.cs
using System;
using System.Windows.Forms;
namespace MinimalExample
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Gui gui = new Gui();
Worker worker = new Worker(gui);
worker.start();
gui.show();
}
}
}
Gui.cs
using System.Windows.Forms;
namespace MinimalExample
{
class Gui
{
private readonly DialogForm _dialog_form;
private readonly MainForm _main_form;
public Gui()
{
_dialog_form = new DialogForm();
_main_form = new MainForm(_dialog_form);
}
public void addLine(string line)
{
_dialog_form.addLine(line);
}
public void show()
{
Application.Run(_main_form);
}
}
}
MainForm.cs
using System;
using System.Windows.Forms;
namespace MinimalExample
{
public partial class MainForm : Form
{
private readonly DialogForm _dialog_form;
public MainForm(DialogForm form)
{
_dialog_form = form;
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
_dialog_form.ShowDialog(this);
}
}
}
DialogForm.cs
using System;
using System.Windows.Forms;
namespace MinimalExample
{
public partial class DialogForm : Form
{
public DialogForm()
{
InitializeComponent();
_ = Handle;
_ = textBox1.Handle;
/* Visible = true;
Visible = false; */
}
public void addLine(string line)
{
Action action = () =>
{
textBox1.AppendText(line);
textBox1.AppendText(Environment.NewLine);
};
if (InvokeRequired)
textBox1.BeginInvoke(action);
else
action();
}
}
}
Worker.cs
using System;
using System.Threading;
namespace MinimalExample
{
internal class Worker
{
private readonly Gui _gui;
private readonly Thread _thread;
internal Worker(Gui gui)
{
_gui = gui;
_thread = new Thread(new ThreadStart(working));
_thread.IsBackground = true;
}
private void working()
{
while (true)
{
if (_gui != null)
_gui.addLine("Test");
else
Console.WriteLine("Test");
Thread.Sleep(1000);
}
}
public void start()
{
_thread.Start();
}
}
}
这是怎么回事 而且,有没有一种方法可以将Gui
和Worker
分开,却没有这些运行时异常?
线程部分在这里不是问题。您只需要以稍微不同的方式处理表单创建:
首次设置:
InitializeComponent()
(该净源代码相关的方法调用更有趣,还要注意调用UpdateHandleWithOwner() )。BeginInvoke()
Form的AddLine()
方法,因为方法调用是在另一个线程中生成的(Gui
对象由Worker
对象线程使用)。public bool CanWrite
::Worker类可以检查此属性以确定应将其输出写入何处。dialogForm != null && dialogForm.Visible;
,因为↓:ShowDialog()
:这意味着当DialogForm关闭时,不处理该Form。同样,该对象在Gui类中仍具有引用。关闭时,其Visible
属性返回false
。第二种设置:
dialogForm != null && dialogForm.IsVisible
just dialogForm != null
,然后在Gui.AddLine()
调用之前验证句柄状态,如果当前没有可用的句柄,则缓存文本行。DialogForm.ShowDialog()
返回时重新创建其句柄。由于ShowDialog()
用于显示DialogForm,因此不处理该表单。重新创建句柄不会导致子控件丢失其内容。实施两个选项:
该IsVisible
属性检查可能会变成Gui的“ Property”,例如bool UpdateOnDialogVisible
,以进行测试,CanWrite,
以便根据此属性的状态将文本写入TextBox。
经过测试:
-Windows 7(我没有WinXP计算机)
-.Net Framework 4.0
-C#5
在Program.cs中:
class Program
{
private static Worker worker = null;
private static Gui gui = null;
// [...]
gui = new Gui();
worker = new Worker(gui);
worker.start();
gui.Show();
}
在Gui.cs中
public class Gui
{
private StringBuilder sb = null;
// [...]
public Gui() {
sb = new StringBuilder();
dialogForm = new DialogForm();
mainForm = new MainForm(dialogForm);
}
public bool CanWrite {
get { return dialogForm != null }
// Or, with the condition that the Dialog is already visible:
// get { return dialogForm != null && dialogForm.Visible; }
}
public void AddLine(string line) {
sb.AppendLine(line);
// Safety measure: cache if the handle is not available at this time
if (this.CanWrite && dialogForm.IsHandleCreated) {
dialogForm.BeginInvoke(new MethodInvoker(() => {
dialogForm.AddLine(sb.ToString());
sb.Clear();
}));
}
}
// [...]
}
在Worker.cs中:
internal class Worker
{
// [...]
private void working() {
while (true) {
if (gui != null && gui.CanWrite) {
gui.AddLine("Test");
}
else {
Console.WriteLine("Test");
}
Thread.Sleep(1000);
}
}
// [...]
}
在DialogForm.cs中:
public partial class DialogForm : Form
{
private TextBox textBox1;
public DialogForm() {
InitializeComponent();
this.CreateHandle();
}
public void AddLine(string line) {
if (this.IsDisposed || !this.IsHandleCreated) return;
this.textBox1.AppendText(line);
this.textBox1.ScrollToCaret();
}
public void RecreateWindow() {
this.CreateHandle();
}
private void InitializeComponent() {
// [...]
}
}
在MainForm.cs中:
public partial class MainForm : Form
{
private Button button1;
internal readonly DialogForm dialogForm = null;
public MainForm() : this(null) { }
public MainForm(DialogForm form) {
dialogForm = form;
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
button1.Enabled = false;
if (dialogForm != null) dialogForm.ShowDialog(this);
button1.Enabled = true;
// As describe in the notes, if a, e.g., UpdateOnDialogVisible () property is
// created, call this method when this property is true, to show text on this
// Window only when is Visible.
dialogForm.RecreateWindow();
}
private void InitializeComponent() {
// [...]
}
}
它是这样工作的:
►第一选择:
►第二个选项:
创建Gui类时,控制台输出始终定向到指定的表单。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句