【问题标题】:Issue while invoking a form from another using System.Window.Forms.Invoke(delegate)使用 System.Window.Forms.Invoke(delegate) 从另一个表单调用表单时出现问题
【发布时间】:2013-07-23 05:23:02
【问题描述】:

我有两种形式和 1 个单例类。我正在初始化formA的btn_A_Click中的单例类。

public partial class frmA : Form
{
    public frmA()
    {
        InitializeComponent();
        frmB frmB;
    }

    private void btn_A_Click(object sender, EventArgs e)
    {
        SessionMgmt.GetInstance().StartFormB(); 
    }
}

这是我的单例类,在这里我尝试使用 Forms.Invoke() 方法。

public class SessionMgmt
{
    static SessionMgmt _sessinMgr;
    frmB frB;

    private SessionMgmt()
    {
        frB = new frmB();
    }

    public static SessionMgmt GetInstance()
    {
        if (_sessinMgr != null)
            return _sessinMgr;
        else
        {
            _sessinMgr = new SessionMgmt();
            return _sessinMgr;
        }
    }

    public bool StartFormB()
    {
        frB.Invoke(new EventHandler(DisplayFrmB));
        return true;
    }

    private void DisplayFrmB(Object o, EventArgs e)
    {
        frB.Visible = true;
        frB.Refresh();
    }

}

这是我的表单B。

public partial class frmB : Form
{
}

但是从frB.Invoke(new EventHandler(DisplayFrmB)); 方法中它会抛出以下异常:

在创建窗口句柄之前,不能对控件调用 Invoke 或 BeginInvoke。

我无法解决问题,如果我遗漏了什么,请帮助或建议我。

编辑

以下结构是我当前项目显示下一个表单的方式。它是由 VB.NET 完成的,我需要在使用 C# 的新项目中使用类似的东西。我看到了 Invoke 函数,它指向一个事件,然后指向一个函数。在该函数中,它只是使 Form.Visible=true 和 Form.Refresh。但为了理解,我只是尝试了一个 POc 并遵循相同的步骤,但尚未解决。

【问题讨论】:

  • 你能发布你试图复制的最低限度的 vb.net 代码吗?

标签: c# .net multithreading winforms


【解决方案1】:

调用调用的原因是什么?这不适合你吗?

public bool StartFormB()
{
    frB.Visible = true;
    return true;
}

【讨论】:

  • @VeeKayBee:为什么需要使用Invoke
  • 我倾向于同意。为什么需要Invoke?表单上该函数的唯一目的是确保您在创建表单的线程上运行代码(因为 Windows 具有线程关联性,如果您不这样做,.NET 将引发异常)。但是如果你还没有创建表单,你就不能在错误的线程上运行,除非你从后台线程调用StartFormB。如果仅从按钮单击处理程序中调用它,则您将在与表单 A 相同的线程上运行。
  • 再澄清一点,如果尚未创建表单,从技术上讲,您根本不可能在错误的线程上。但是,如果您只想拥有一个带有事件循环的线程,那么您需要确保您在表单 A 的线程上。如果您需要这样做,请在表格 A 上Invoke
【解决方案2】:

Windows 窗体是 Windows API 的包装器,该异常意味着尚未创建基础窗口。我认为它是在您第一次将Visible 设置为true 时创建的,还有其他一些情况会这样做。

请参阅此链接以获取可能的解决方案:http://blogs.msdn.com/b/mapo/archive/2011/04/27/forcing-handle-creation-in-a-net-windows-forms-control.aspx

【讨论】:

    【解决方案3】:

    该异常有两个可能的原因:

    • 调用调用时未创建表单
    • 您可能在错误的线程上创建控件

    你应该在调用之前检查InvokeRequired属性,当然在此之前检查null

    public bool StartFormB()
    {
        if (frB == null)
        {
            throw new ArgumentNullException("frB");
        }
    
       if (frB.InvokeRequired)
       {
    
            frB.Invoke(new EventHandler(DisplayFrmB));
       }
       else
       {    
          if (frB.IsDisposed)
          {
            throw new ObjectDisposedException("Control is already disposed.");
          }
      }
    
      return true;
    }
    

    【讨论】:

    • 如果InvokeRequiredfalse,则调用Invoke 实际上并没有害处。
    • 该页面显示InvokeRequired 可以返回false,如果调用将发生在同一个线程上或句柄尚未创建。它仅在IsHandleCreated 将返回false 的情况下有害。否则,它只会在同一个线程上进行调用。 Invoke 几乎可以肯定是 SendMessage 的包装。鉴于此处的代码,将不会创建句柄。对InvokeRequired 的调用将返回false,并且不会按需要显示表单。
    • @LiranElisha DisplayFrmB 是什么?
    【解决方案4】:

    如果控件的 Visible 属性为 false,则不会创建控件句柄。当您调用 Invoke 时,您在委托中将控件的可见状态设置为 true,但尚未创建句柄,因此您不能调用 Invoke。所以 - 你必须调用 frB.CreateHandle();之后:frB = new frmB();强制创建控制句柄

        private SessionMgmt()
        {
            frB = new frmB();
            var h = frB.Handle;
        }
    

    【讨论】:

    • 我明白了。如果控件的 Visible 属性为 false,则不会创建控件句柄。当您调用 Invoke 时,您在委托中将控件的可见状态设置为 true,但尚未创建句柄,因此您不能调用 Invoke。所以 - 你必须调用 frB.CreateHandle();之后:frB = new frmB();强制创建控制句柄
    • @AndriyVandych:CreateHandle() 不是公共方法。但是您可以通过查询 Handle 属性来调用它 - var h = frB.Handle;
    • @YK1:你说得对,是我的错。调用 Handle 属性是个好方法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-15
    • 1970-01-01
    • 2012-02-24
    相关资源
    最近更新 更多