使用C#和WinForms在主窗体的单独线程上创建一个新的临时表单

时间:2014-03-12 00:35:21

标签: c# multithreading winforms

在找到这种特定情况的答案时遇到一些麻烦。我需要创建一个临时表单(稍后将被销毁),该表单与主表单位于一个单独的线程中。

此表单用于向用户显示帐户登录信息。同时该表格在屏幕上,还向用户显示模态输入框。模态输入框的存在可防止与登录信息表单(复制/粘贴)的任何交互,这是用户的必要功能。

我怎么能:

A)在主表单的完全独立的线程上创建并显示一个新表单?

B)一旦用户在模态对话框中输入了输入,就从主窗体的线程中销毁该窗体?

注意:我已经探索过MainForm.Invoke / BeginInvoke,但这并没有提供我需要的结果,正如其他一些帖子所声称的那样。

模态InputBox的代码:

class InputBox
{
    public static DialogResult Show(string prompt, bool hideInput, out string userInput, Form parent = null)
    {
        InputBoxForm frm = new InputBoxForm(prompt, hideInput);

        if (parent != null)
            frm.ShowDialog(parent);
        else
            frm.ShowDialog();

        if (frm.DialogResult == DialogResult.OK)
        {
            userInput = frm.txtInput.Text;
            frm.Dispose();
            return DialogResult.OK;
        }
        else
        {
            userInput = "";
            frm.Dispose();
            return DialogResult.Cancel;
        }
    }
}

程序中使用的代码:

Form loginDisplay = LoginInfoForm(user, pass);
loginDisplay.Show(null);
string input = "";
InputBox.Show("Enter info:", false, out input, parent: this);

LoginInfoForm只是一个动态创建表单并将其格式化的函数。

2 个答案:

答案 0 :(得分:4)

这是一种有点人为的情况,IIUIC。为什么在单独的线程上需要新表单?

您仍然可以在主UI线程上同时拥有模式对话框(主窗体的父级)和无模式弹出窗体。用户将能够独立地与两者进行交互。

只需指定对话框的正确父级:dialogForm.ShowDialog(mainForm),无模式窗口的父级:form.Show(null)

在任何一种情况下,这种用户界面都可能会让用户感到困惑。

已更新,以下是我所描述的一个示例,其中包含一项重要修订。实际上, Form.ShowDialog会禁用同一个线程拥有的所有顶级可见和启用的窗口(而不是仅禁用对话框的直接父窗口,因为它的Win32对应DialogBox一样)。

不可否认,这对我来说是一个意想不到的行为,虽然我看到了背后的原因:我上面提到的一致的UI体验。有关更多详细信息,请参阅ModalApplicationContext.DisableThreadWindows的实现。

解决方法非常简单:如果弹出窗体当前可见,请在显示对话框之前将其禁用,并在显示对话框时重新启用它。请注意,它们都在同一个线程上完成:

var dialog = new ModalDialog { Width = 200, Height = 100 };

if (popup != null)
{
    popup.Enabled = false;
    dialog.Load += delegate { 
        popup.Enabled = true; };
}

dialog.ShowDialog(this);

完整的WinForms应用

using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsForms_22340190
{
    public partial class MainForm : Form
    {
        public MainForm()
        {
            InitializeComponent();

            var cts = new CancellationTokenSource();

            this.Load += async (s, e) =>
            {
                // start the background thread in 1s
                await Task.Delay(1000);

                Form popup = null;

                var task = Task.Run(() => 
                {
                    // background thread
                    this.Invoke(new Action(() => 
                    {
                        // create a popup on the main UI thread
                        popup = new Popup { Width = 300, Height = 200 };
                        popup.Show(this);
                    }));

                    // imitate some work
                    var i = 0;
                    while (true)
                    {
                        Thread.Sleep(1000);
                        cts.Token.ThrowIfCancellationRequested();

                        var n = i++;
                        this.BeginInvoke(new Action(() =>
                        {
                            // update the popup UI on the main UI thread
                            popup.Text = "Popup, step #" + n;
                        }));
                    }
                });

                // wait 2s more and display a modal dialog
                await Task.Delay(2000);

                var dialog = new ModalDialog { Width = 200, Height = 100 };

                if (popup != null)
                {
                    popup.Enabled = false;
                    dialog.Load += delegate { 
                        popup.Enabled = true; };
                }

                dialog.ShowDialog(this);
            };

            this.FormClosing += (s, e) =>
                cts.Cancel();
        }
    }

    public partial class ModalDialog : Form
    {
        public ModalDialog() 
        { 
            this.Text = "Dialog";
            this.Controls.Add(new TextBox { Width = 50, Height = 20 });
        }
    }

    public partial class Popup : Form
    {
        public Popup() 
        { 
            this.Text = "Popup";
            this.Controls.Add(new TextBox { Width = 50, Height = 20 });
        }
    }
}

答案 1 :(得分:0)

更新了InputBox类的代码:

    public static DialogResult Show(string prompt, bool hideInput, out string userInput, Form parent = null, Form enable = null)
    {
        InputBoxForm frm = new InputBoxForm(prompt, hideInput);

        if (enable != null)
        {
            frm.Load += delegate { enable.Enabled = true; };
        }

        if (parent != null)
            frm.ShowDialog(parent);
        else
            frm.ShowDialog();

        if (frm.DialogResult == DialogResult.OK)
        {
            userInput = frm.txtInput.Text;
            frm.Dispose();
            return DialogResult.OK;
        }
        else
        {
            userInput = "";
            frm.Dispose();
            return DialogResult.Cancel;
        }
    }

程序中的更新代码:

                Form loginDisplay = null; 

                this.Invoke(new Action(() =>
                    {
                        loginDisplay = LoginInfoForm(user, pass);
                        loginDisplay.Show(this);
                    }));

                if (loginDisplay2 != null)
                {
                    loginDisplay2.Enabled = false;
                }

                string input = "";
                InputBox.Show("Input info", false, out input, parent: this, enable: loginDisplay);

感谢@Noseratio提供了解决方案的代码。