【问题标题】:C# Windows Form created by EventHandler disappears immediatelyEventHandler 创建的 C# Windows 窗体立即消失
【发布时间】:2009-05-28 16:34:30
【问题描述】:

我不知道为什么会这样,但是当我在 EventHandler 中创建一个新表单时,它会在方法完成后立即消失。

这是我的代码。为了清楚起见,我对其进行了编辑,但从逻辑上讲,它是完全相同的。

static void Main()
{
    myEventHandler = new EventHandler(launchForm);
    // Code that creates a thread which calls
    // someThreadedFunction() when finished.
}

private void someThreadedFunction()
{
    //Do stuff

    //Launch eventhandler
    EventHandler handler = myEventHandler;
    if (handler != null)
    {
        handler(null, null);
        myEventHandler = null;
    }
}

private void launchForm(object sender, EventArgs e)
{
    mf = new myForm();
    mf.Show();
    MessageBox.Show("Do you see the form?");
}

private myForm mf;
private EventHandler myEventHandler;

只要 MessageBox 显示“你看到表单了吗?”,新表单就会显示。有没有。只要我点击确定,表单就会消失。

我错过了什么?我认为通过将新表单分配给类变量,它会在方法完成后保持活动状态。显然,情况并非如此。

【问题讨论】:

  • 澄清:someThreadedFunc 是在主 UI 线程以外的线程上调用的(所以 launchForm 也是)?
  • 正确。对不起。我尽量不贴3页代码就说清楚了。

标签: c# winforms multithreading .net-2.0 event-handling


【解决方案1】:

我认为问题在于您正在从自定义线程的处理程序中执行代码,而 不是 UI 线程,这是必需的,因为它操作 Windows 消息泵。您想在此处使用 Invoke 方法来确保表单在 UI 线程上得到并显示。

private void launchForm(object sender, EventArgs e)
{
    formThatAlreadyExists.Invoke(new MethodInvoker(() =>
    {
        mf = new myForm();
        mf.Show();
        MessageBox.Show("Do you see the form?");
    }));
}

请注意,这假定您已经有一个使用Application.Run 运行的 WinForms 对象(称为formThatAlreadyExists)。此外,可能有更好的地方将 Invoke 调用放入您的代码中,但这至少是一个可以使用的示例。

【讨论】:

  • 我尝试了你的建议。但是,我收到编译器错误:无法将 lambda 表达式转换为类型“System.Delegate”,因为它不是委托类型。它突出显示 Invoke 之后的第二个左括号。
  • @Andrew:对不起,应该已经测试过了。您需要将 lambda 表达式包装在 MethodInvoker 类型中,因为参数类型是 System.Delegate。现在应该可以正常工作了。
  • 嗯,编译好了,但现在我明白了:System.InvalidOperationException:在创建窗口句柄之前,不能在控件上调用 Invoke 或 BeginInvoke。在 System.Windows.Forms.Control.MarshaledInvoke(控制调用者,委托方法,Object[] args,布尔同步)在 System.Windows.Forms.Control.Invoke(Delegate 方法,Object[] args)在 launchForm(对象发送者, EventArgs e)
  • 我尝试过这样做:this.Invoke。也许这就是问题所在。所有这些代码都是从另一个 Windows 窗体运行的,所以我认为 this.Invoke 应该可以工作。我想我需要为初始表单创建一个句柄并使用它,但我不知道如何。
  • @Andrew:我不这么认为。只需确保在启动此线程之前已创建并加载了表单。 (即,如果是这种情况,请从 Form_Load 之类的地方而不是构造函数启动它。)然后 InvalidOperationException 错误应该消失。
【解决方案2】:

我认为如果您在线程上创建表单,则该表单归该线程所有。创建任何 UI 元素时,应始终在主 (UI) 线程上完成。

【讨论】:

    【解决方案3】:

    这看起来好像您不在表单 sta 线程上,所以一旦您显示表单它就消失了并且线程完成它的工作它会自行杀死它,因为没有任何引用线程。它不是最好的解决方案,但如果你需要一个代码示例,你可以使用 showdialog() 而不是 show 来完成它保持状态,我将这个完全相同的过程用于“加载......”表单

    public class Loading
    {
        public delegate void EmptyDelegate();
        private frmLoadingForm _frmLoadingForm;
        private readonly Thread _newthread;
        public Loading()
        {
            Console.WriteLine("enteredFrmLoading on thread: " + Thread.CurrentThread.ManagedThreadId);
            _newthread = new Thread(new ThreadStart(Load));
            _newthread.SetApartmentState(ApartmentState.STA);
            _newthread.Start();
        }
    
        public void Load()
        {
            Console.WriteLine("enteredFrmLoading.Load on thread: " + Thread.CurrentThread.ManagedThreadId);
            _frmLoadingForm = new frmLoadingForm();
            if(_frmLoadingForm.ShowDialog()==DialogResult.OK)
            {
    
            }
        }
    
    
        /// <summary>
        /// Closes this instance.
        /// </summary>
        public void Close()
        {
            Console.WriteLine("enteredFrmLoading.Close on thread: " + Thread.CurrentThread.ManagedThreadId);
            if (_frmLoadingForm != null)
            {
                if (_frmLoadingForm.InvokeRequired)
                {
                    _frmLoadingForm.Invoke(new EmptyDelegate(_frmLoadingForm.Close));
                }
                else
                {
                    _frmLoadingForm.Close();
                }
            }
            _newthread.Abort();
        }
    }
    public partial class frmLoadingForm : Form
    {
    
        public frmLoadingForm()
        {
            InitializeComponent();
        }
    }
    

    【讨论】:

    • 清理了一下,正如我所说,虽然我不推荐你在做什么,但我能理解你为什么这样做,否则我不会有这个代码方便,这将完成你想要的 frmLoadingForm 是在您的情况下,myForm,我创建了另一个类来包装我的调用,以尝试像这样更干净的代码:)
    • 控制台写入让您了解调用的工作原理以及何时使用它,以便您晚上尝试将来的线程操作,您会看到为什么它在当前代码中失败
    【解决方案4】:

    dbf.Show();
    

    一个错字?应该是这个吧?

    mf.Show(); 
    

    除了您打算展示的表格之外,您还展示了另一种表格吗?

    【讨论】:

    • 是的 - 这是一个错字。对不起。
    【解决方案5】:

    您在非 UI 线程上创建了一个窗口。当线程中止时,它会带走你的窗口。故事结局。 在传递一个委托的主窗体上执行调用,该委托将执行在 UI 线程上创建消息框的方法。 由于 MessageBox 是一个模态窗口,如果不希望 launchForm 方法阻塞后台线程,请创建一个具有所需 UI 的自定义表单并在其上调用 show(),而不是 ShowDialog()。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-12-03
      • 1970-01-01
      • 1970-01-01
      • 2010-10-30
      相关资源
      最近更新 更多