如何在C#控制台应用程序中创建一个无模式对话框

3

我想用以下代码创建一个非模态对话框。 但是,创建后窗体似乎没有响应。 我猜测如果按这种方式创建,则消息循环可能被阻塞。 有人知道如何以正确的方式创建它吗?

class Program
{
    static void Main(string[] args)
    {
        Form form = new Form();
        form.Show();
        Console.ReadLine();
    }
}

你肯定是想说非模态吧?Application.Run() 是必须的,但不需要在主线程上调用。而且,对于 UI 线程,Thread.SetApartmentState() 是一个硬性要求。这使得 ShowDialog() 成为一个 bug,而且很难诊断。请使用 Thread 类。 - Hans Passant
@Hans Passant 是的,你是对的。谢谢 - shi frank
2个回答

4

显示模态和非模态窗体:

要将窗体显示为非模态对话框,请调用Show方法:
以下示例演示如何以非模态格式显示关于对话框。

// C#
//Display frmAbout as a modeless dialog
Form f= new Form();
f.Show();

要将表单显示为模态对话框,请调用ShowDialog方法。
以下示例演示如何以模态方式显示对话框。
// C#
//Display frmAbout as a modal dialog
Form frmAbout = new Form();
frmAbout.ShowDialog();

请查看以下控制台应用程序代码:
See: 显示模式对话框和非模式对话框的 Windows 窗体


请见以下控制台应用程序代码:

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

class Test
{
    [STAThread]
    static void Main()
    {
        var f = new Form();
        f.Text = "modeless ";
        f.Show();

        var f2 = new Form() { Text = "modal " };

        Application.Run(f2);
        Console.WriteLine("Bye");

    }
}

你可以使用另一个线程,但是必须等待该线程加入或中止它:像这样的工作示例代码:

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

namespace ConsoleApplication2
{
    static class Test
    {
        [STAThread]
        static void Main()
        {
            var f = new Form { Text = "Modeless Windows Forms" };
            var t = new Thread(() => Application.Run(f));
            t.Start();

            // do some job here then press enter
            Console.WriteLine("Press Enter to Exit");
            var line = Console.ReadLine();
            Console.WriteLine(line);

            //say Hi
            if (t.IsAlive) f.Invoke((Action)(() => f.Text = "Hi"));

            if (!t.IsAlive) return;
            Console.WriteLine("Close The Window");
            // t.Abort();
            t.Join();
        }
    }
}

是的,你的代码可以工作。但是 Application.Run 会阻塞线程。所以我必须将它移动到另一个线程中。 - shi frank
@shi-frank:是的,你可以使用另一个线程,但你必须等待该线程加入或中止它,参见最后一个示例代码。 - user6169399

1

终于,我搞定了。 为了解除主线程的阻塞,必须使用新线程并调用Application.Run来为窗体创建消息泵。 现在窗体和主线程都活着了。 感谢大家。

class Program
{

    public static void ThreadProc(object arg)
    {
        Form form = arg as Form;
        Application.Run(form);
    }

    [STAThread]
    static void Main(string[] args)
    {
        Form form = new Form() { Text = "test" };

        Thread t = new Thread(ThreadProc);
        t.Start(form);
        string line = Console.ReadLine();
        Console.WriteLine(line);

        form.Close();
    }
}

如果在关闭窗体之前按下Enter键会发生什么:您必须等待另一个线程加入,请参见我的最后一个示例代码:System.InvalidOperationException未处理 - user6169399
没有使用t.Join()我没有遇到这个错误;实际上,Application.Run将会阻塞直到form.Close()被调用。因此,在form.Close之前Join将会造成死锁。 - shi frank
嗯,Join()在Close()之前是错误的。这不会导致死锁,您仍然可以关闭GUI上的窗体,但是当窗体已经关闭并且可能已被处理后,Close()将被调用。另一方面,在Close()之后使用Join()则没问题。 - Emperor Orionii

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接