简体   繁体   English

使用C#和WinForms在与主窗体不同的线程上创建新的临时窗体

[英]Creating a new, temporary Form on a separate thread from the main form using C# and WinForms

Having some trouble finding an answer to this specific situation. 在找到针对此特定情况的答案时遇到一些麻烦。 I need to create a temporary form (that will later be destroyed) that is in a separate thread from the main form. 我需要创建一个临时表单(稍后将销毁),该表单与主表单处于单独的线程中。

This form is used for displaying account login information to the user. 该表格用于向用户显示帐户登录信息。 At the same time this form is on screen, a modal input box is also displayed to the user. 同时在屏幕上显示此表单,同时还会向用户显示模式输入框。 The presence of the modal input box prevents any interaction with the login info form (copying/pasting), which is necessary functionality for the user. 模态输入框的存在可防止与登录信息表单进行任何交互(复制/粘贴),这是用户必需的功能。

How can I: 我怎样才能:

A) Create and display a new Form on an entirely separate thread from the main Form? A)在与主表单完全独立的线程上创建并显示新表单吗?

B) Destroy that Form from the main Form's thread once the user has entered input into the modal dialog box? B)一旦用户在模态对话框中输入了输入,便从主窗体的线程中销毁该窗体?

Note: I have already explored MainForm.Invoke/BeginInvoke and this does not give the results I need, as some other posts have claimed it should. 注意:我已经浏览过MainForm.Invoke / BeginInvoke,但这并不能提供我需要的结果,正如其他一些帖子所声称的那样。

The code for the modal 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;
        }
    }
}

And the code as it is used in the program: 以及程序中使用的代码:

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

The LoginInfoForm is just a function that dynamically creates a form and formats it a bit. LoginInfoForm只是一个函数,它可以动态创建表单并对其进行格式化。

This is a bit contrived situation, IIUIC . IIUIC这是一个人为的情况。 Why do you need a new form on a separate thread? 为什么需要在单独的线程上创建新表单?

You still can have a modal dialog (parented by the main form) and a modeless pop-up form at the same time on the main UI thread. 在主UI线程上,您仍然可以同时具有模式对话框(由主窗体作为父级)和无模式的弹出窗体。 The user will be able to interact with both independently. 用户将能够独立地与两者进行交互。

Just specify the corrent parent to the dialog: dialogForm.ShowDialog(mainForm) , and no parent to the modeless form: form.Show(null) . 只需为对话框指定相应的父对象: dialogForm.ShowDialog(mainForm) ,而无模形式的父对象没有指定对象: form.Show(null)

In either case, this kind of UI might be confusing to the user. 无论哪种情况,这种UI都会使用户感到困惑。

Updated , below is an example of what I've described, with one important amendment. 以下是更新的 ,是我所描述的示例,并做了一项重要的修改。 Indeed, Form.ShowDialog disables all top-level visible and enabled windows owned by the same thread (rather than disabling only the direct parent window of the dialog, as its Win32 counterpart DialogBox does). 实际上, Form.ShowDialog禁用同一线程拥有的所有顶级可见和启用的窗口 (而不是像Win32对话框DialogBox那样仅禁用对话框的直接父窗口)。

Admittedly, this is quite an unexpected behavior for me, although I see the reason behind it: the consistent UI experience I mentioned above. 诚然,这对我来说是一个出乎意料的行为,尽管我知道其背后的原因:我上面提到的一致的UI体验。 For more details, refer to the implementation of ModalApplicationContext.DisableThreadWindows . 有关更多详细信息,请参考ModalApplicationContext.DisableThreadWindows的实现。

The workaround is very simple: if the popup form is currently visible, disable it before showing displaying the dialog, and re-enable it when the dialog is shown. 解决方法非常简单:如果弹出窗口当前可见,请在显示对话框之前将其禁用,并在显示对话框时将其重新启用。 Note it's all done on the same thread: 请注意,它们都是在同一线程上完成的:

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

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

dialog.ShowDialog(this);

The complete WinForms app: 完整的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 });
        }
    }
}

Updated code for InputBox class: 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;
        }
    }

Updated code within the program: 程序中的更新代码:

                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);

Thanks to @Noseratio for the code that led me to the solution. 感谢@Noseratio提供了导致我找到解决方案的代码。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM