【问题标题】:Cant call ParametizedThread from Child Form无法从子窗体调用 ParametizedThread
【发布时间】:2020-08-21 16:57:01
【问题描述】:

我正在开发一个客户端/服务器应用程序。 在客户端应用程序中,我有一个作为 MDI 父级的主窗体。 主窗体有一个 Load 事件,它创建一个子窗体的新实例并使其可见。 该事件还将 Main 确立为 Child 的 MdiParent。 Child 表单是一个登录屏幕。 从子窗体中,我创建了对父窗体的引用,以便能够从父窗体调用方法。

但是,在执行 MdiParent.RequestConnection 方法时,GUI 会卡住。 所以我尝试从线程执行该方法,但它不接受我的声明,即使我似乎遵循正确的语法。 我不明白我做错了什么。请帮忙

主窗体

public partial class frmMainForm: Form
    {
        
        public frmMainForm()
        {
            InitializeComponent();
           
        }
        Thread runningClient;
        public MyTcpClient client= new MyTcpClient ();
        frmChildForm frmSignIn;
        bool clientConnected;
       

        private void frmMainForm_Load(object sender, EventArgs e)
        {
            clientConnected= false;
            panelSidebar.Hide();
            if(frmSignIn == null)
            {
                frmSignIn= new frmChildForm();
                frmSignIn.MdiParent = this;
                
                frmSignIn.Show();
            }
        }

        public void TurnOnPanels()
        {
            panelSidebar.Visible = true;
            panelSidebar.BringToFront();
        }

        public void RequestConnection(string username)
        {
            
            string serverRsp = client.Connect(username);
            if(serverRsp.Equals("SUCCESS")) 
            {
                MessageBox.Show("Signed In", "Welcome", MessageBoxButtons.OK, MessageBoxIcon.Information);
                clientConnected = true;
                frmSignIn.Close();
            }
        }
    }

还有我的孩子表格

public partial class frmChildForm : Form
    {
        frmMainForm frmParent;
        Thread clientRunning;
        public frmChildForm()
        {
            InitializeComponent();
            frmParent= (frmMainForm)this.MdiParent;
        }

        private void frmSignIn_FormClosing(object sender, FormClosingEventArgs e)
        {
            frmParent= (frmMainForm)this.MdiParent;
            frmParent.TurnOnPanels();
        }

        private void btnSignIn_Click(object sender, EventArgs e)
        {
            if (txtSignInUsername.Text.Equals(""))
            {
                MessageBox.Show("No empty fields.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            } 
            else
            {
                //This is where it fails
                clientRunning= new Thread(new ParameterizedThreadStart(frmParent.RequestConnection);
                //"No Overload for RequestConnection matches delegate ParameterizedThreadStart"
                //If I try to include the parameter inside that call, I get a "waiting for method                      //name" syntax error message instead.
                clientRunning.Start(txtSignInUsername.Text.ToUpper());
            }
        }

        private void frmSignIn_Load(object sender, EventArgs e)
        {
            frmParent = (frmMainForm)this.MdiParent;
        }
    }

我还尝试通过在 RequestConnection 中创建一个线程来从主窗体执行此操作,它应该执行 client.Connect,但我遇到了同样的错误。

【问题讨论】:

  • 您不能从非 UI 线程创建、访问或更新任何 UI 元素。

标签: c# multithreading winforms tcpclient tcpserver


【解决方案1】:

你需要修复的几件事

public void RequestConnection(object username) // changed parameter type
{
    if (username == null)
        throw new ArgumentNullException(nameof(username));

    if (!(username is string))
        throw new InvalidCastException("Expect string");// give proper message

    string serverRsp = client.Connect(username.ToString());
    if (serverRsp.Equals("SUCCESS"))
    {
        MessageBox.Show("Signed In", "Welcome", MessageBoxButtons.OK, MessageBoxIcon.Information);
        clientConnected = true;

        //this is require to solve cross-thread operation
        if (this.InvokeRequired)
            this.Invoke(new MethodInvoker(delegate ()
            {
                frmSignIn.Close();
            }));
        else
            frmSignIn.Close();
    }
}

您需要在 Form Load Event 中获取 MdiParent 并从 Constructor 中移除。

您的子表单加载事件/或使用父更改事件

private void FrmLogin_Load(object sender, EventArgs e)
{
    frmParent = (MainForm)this.MdiParent;
}

【讨论】:

  • 谢谢,虽然这可以执行线程,但登录表单实际上并没有关闭,所以它看起来不像在执行 frmSignIn
  • 嗨...抱歉。我无法理解你的评论。你能解释一下你仍然面临的问题吗?
  • 如果我的回答完全满足您的问题,请将其标记为已接受的答案。这将帮助其他人找到答案,而不是检查线程中的所有答案。
  • 谢谢我刚刚以为答案中缺少某些东西,因为表单没有关闭,但我发现这是由于一个不相关的问题(连接过程从未完成,因为它被卡住等待来自服务器的响应)。你的回答有效
  • 好..我在发送前测试了它。您是否面临任何其他可以提出的问题。我会尽力解决的。快乐的编码兄弟..
【解决方案2】:

ParametrizedThreadStart 委托实际上接受一个对象作为参数。

所以你必须为它提供一个将对象作为参数而不是字符串的方法。在您的方法中,您将收到对象,检查它是否为字符串,然后将其转换为字符串。

【讨论】:

  • 如何处理方法的返回值?我的 Connect 方法返回一个字符串,我的程序根据该字符串的内容做出反应。如果我从像 thread.Start(username) 这样的子线程调用该方法,我将无法存储该返回值
  • @Elmambo2194 你问的是一个不同的问题。我建议你开始阅读一些关于线程如何在 C# 中工作的文档。如果您需要返回值,请改用 Tasks。但首先要阅读有关所有这些工作原理的文档。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多