Windows窗体:一种由应用程序而不是用户打开/关闭的模式窗体?
我有一个结构相当不错的.NET 3.5表单应用程序(单元测试,依赖注入,SoC,表单只是中继输入和显示输出,并且不执行任何逻辑,yadda yadda),我只是想念winforms有关如何使这一点起作用的知识。
与数据库的连接丢失时,我经常检测到并处理它,并且希望弹出模式表格,从而阻止应用程序的使用,直到重新建立连接为止。我不是100%不确定该怎么做,因为我不是在等待用户输入,而是在使用计时器轮询数据库。
我的尝试是设计一个带有标签的表单,并执行以下操作:
partial class MySustainedDialog : Form { public MySustainedDialog(string msg) { InitializeComponent(); lbMessage.Text = msg; } public new void Show() { base.ShowDialog(); } public new void Hide() { this.Close(); } } public class MyNoConnectionDialog : INoConnectionDialog { private FakeSustainedDialog _dialog; public void Show() { var w = new BackgroundWorker(); w.DoWork += delegate { _dialog = new MySustainedDialog("Connection Lost"); _dialog.Show(); }; w.RunWorkerAsync(); } public void Hide() { _dialog.Close(); } }
由于_dialog.Close()是跨线程调用,因此无法使用。我已经能够在Windows窗体中找到有关如何解决此问题的信息,但是在这种情况下(我们需要自行创建窗体),这种信息无法找到。
有人可以给我一些建议,以实现我想要做的事情吗?
编辑:请注意,我只是尝试了背景工作者,因为他缺乏其他想法,因为我对UI线程的工作原理不是很熟悉,因此我完全愿意接受建议。我还应该注意,我不想关闭他们当前正在使用的表单,我只希望它显示在其顶部。就像"确定/取消"对话框一样,但是我可以以编程方式打开和关闭该对话框(并且我需要对其外观进行控制)
解决方案
将所有UI工作保留在主UI线程上而不是使用BackgroundWorker可能更简单吗?不看更多代码就很难说,但是我认为我们不需要。
创建计时器时,可以为其分配Timer.SynchronizingObject,以使其使用主UI线程。坚持下去吗?
抱歉,如果不进一步了解程序的结构,就无法给出更好的答案。
没有理由使用后台工作程序来实际启动表单的新实例,我们只需从UI线程中进行操作即可。
我不确定我们整体方法的正确性,但是要具体回答问题,请尝试将MySustainedDialog Hide()函数更改为以下内容:
public new void Hide() { if (this.InvokeRequired) { this.BeginInvoke((MethodInvoker)delegate { this.Hide(); }); return; } this.Close(); }
在类似情况下,我采用了两种方法。
一种是完全在主UI线程中进行操作。我们可以使用Windows.Forms.Timer实例来执行此操作,该实例将在主UI线程中触发。
好处是简单易用,可以完全访问所有UI组件。缺点是任何阻塞呼叫都会对用户体验产生巨大影响,从而阻止任何用户交互。因此,如果我们需要长时间运行的命令,这些命令最终会导致UI动作(例如,如果检查数据库花费了几秒钟),那么我们就需要进行跨线程操作。
从代码角度来看,最简单的跨线程解决方案是从BackgroundWorker中调用Control.Invoke方法。
调用使我们可以将工作"发布"到控件中,本质上说"请使用我们自己的线程来运行它"。