簡體   English   中英

使用C#和WinForms在與主窗體不同的線程上創建新的臨時窗體

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

在找到針對此特定情況的答案時遇到一些麻煩。 我需要創建一個臨時表單(稍后將銷毀),該表單與主表單處於單獨的線程中。

該表格用於向用戶顯示帳戶登錄信息。 同時在屏幕上顯示此表單,同時還會向用戶顯示模式輸入框。 模態輸入框的存在可防止與登錄信息表單進行任何交互(復制/粘貼),這是用戶必需的功能。

我怎樣才能:

A)在與主表單完全獨立的線程上創建並顯示新表單嗎?

B)一旦用戶在模態對話框中輸入了輸入,便從主窗體的線程中銷毀該窗體?

注意:我已經瀏覽過MainForm.Invoke / BeginInvoke,但這並不能提供我需要的結果,正如其他一些帖子所聲稱的那樣。

模態輸入框的代碼:

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只是一個函數,它可以動態創建表單並對其進行格式化。

IIUIC這是一個人為的情況。 為什么需要在單獨的線程上創建新表單?

在主UI線程上,您仍然可以同時具有模式對話框(由主窗體作為父級)和無模式的彈出窗體。 用戶將能夠獨立地與兩者進行交互。

只需為對話框指定相應的父對象: dialogForm.ShowDialog(mainForm) ,而無模形式的父對象沒有指定對象: form.Show(null)

無論哪種情況,這種UI都會使用戶感到困惑。

以下是更新的 ,是我所描述的示例,並做了一項重要的修改。 實際上, 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 });
        }
    }
}

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提供了導致我找到解決方案的代碼。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM