Windows窗体:一种由应用程序而不是用户打开/关闭的模式窗体?

时间:2020-03-06 14:58:49  来源:igfitidea点击:

我有一个结构相当不错的.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方法。

调用使我们可以将工作"发布"到控件中,本质上说"请使用我们自己的线程来运行它"。