在找到这种特定情况的答案时遇到一些麻烦。我需要创建一个临时表单(稍后将被销毁),该表单与主表单位于一个单独的线程中。
此表单用于向用户显示帐户登录信息。同时该表格在屏幕上,还向用户显示模态输入框。模态输入框的存在可防止与登录信息表单(复制/粘贴)的任何交互,这是用户的必要功能。
我怎么能:
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
只是一个动态创建表单并将其格式化的函数。
答案 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提供了解决方案的代码。