0

我有一个订单。订单完成后,我使用线程将订单通过电子邮件发送给供应商。Thread 用于防止订单导出为 pdf 并发送时系统挂起。

问题:一旦威胁完成且没有错误,我想在 MDIParent Toolstripstatuslabel 上放置一条消息,以确认订单已发送。但我收到一个错误:“System.NullReferenceException:对象引用未设置为对象的实例”。我可能错了,指的是子窗口在关闭时在父窗体上设置了工具条状态标签引用,因此威胁无法再访问它。我知道简单的解决方案是使用 MessageBox 来确认一切顺利......但是如果你能做到优雅,为什么要让它变得简单呢?

所以我的问题是:如何从威胁中引用父表单中的控件?我尝试查看调用,但不确定如何实现它或者它是否实际上是正确的方向。

编辑:

我来自 childform 的代码

public partial class frm_n_order : Form
{
     .
     .

private void bProcess_Click(object sender, EventArgs e)
{
     .
     .
     .

    new Thread(new ThreadStart(delegate
    {
        fExportOrder(strOrderNo);
        fSendMailv2(strPlant, strSupCode, strOrderNo);                                
    })).Start();

    this.close();
}

private void fExportOrder(string strOrderNo)
{
    //export order to pdf
}

private void fSendMailv2(string strPlant, string strSupCode, string strOrderNo);
{
    // get pdf
    // get email address

    try
    {
        // send email
        ((MDIParent1)MdiParent).tsslMain.Text = "Order No:" + strOrderNo + " was successfully send to " + strEmails;  //here I need to find a different way of accessing the Toolstripstatuslabel in the parent form
    }
    catch
    {
        MessageBox.Show("Email did not send");
    }
}

}

编辑:

好的,所以在花了一天多的时间试图弄清楚如何使用 Invoke 之后,我意识到虽然在使用线程时这似乎是一种很好的做法,但这不是我的答案。我的问题与关闭处理所有控件的子窗体直接相关,因此它失去了对 MdiParent 的引用。为了解决这个问题,我做了以下事情:

在我的子班中,我添加了:

public static Form IsFormAlreadyOpen(Type FormType)
{
    foreach (Form OpenForm in Application.OpenForms)
    {
    if (OpenForm.GetType() == FormType)
        return OpenForm;
    }
    return null;
 }

我不认为这是最优雅的解决方案,但理论上是,当我需要访问 Toolstripstatuslabel 时,我的 Parent 表单将始终处于打开状态。所以我基本上遍历所有打开的表单以找到对活动 MdiParent 实例的引用,然后将其传递回调用者。然后在线程中使用以下代码。

 MDIParent1 fm = null;
 if ((fm = (MDIParent1)IsFormAlreadyOpen(typeof(MDIParent1))) != null)
 {
      fm.Toolstripstatuslabel1.Text = "Order No:" + strOrderNo + " was successfully send to " + strEmails;
 }

我仍在寻找更好的方法,但现在这可行。

4

2 回答 2

0

我很难忽视有人说“但如果你能做到优雅,为什么要让它变得容易呢?”

惊人的!

我想如果我们可以优雅地做某事,那么将来应该很容易......对吧?

无论如何,希望您发现以下内容对您有所帮助。

注意:在我看来,您将线程声明为局部变量,而不是将其存储在局部范围之外。如果我们希望某些内容超出范围的末尾,最好存储对它的引用(在下面的示例中使用私有 Task 字段完成)。

当然,线程会被添加到线程池并存储在框架中的某个地方,即使它只是一个局部变量,所以我认为它不会因为垃圾收集而中止,但我不喜欢这个想法我没有参考的浮动实例。

public class MyChildForm : Form
{
    private Task longRunningTask;
    private Task closeTask;

    public string ResultOfTimeConsumingOperation { get; private set; }

    protected override Dispose(bool disposing)
    {
        if (disposing)
        {
             longRunningTask?.Dispose();
             closeTask?.Dispose();
        }
        base.Dispose(disposing);
    }    

    private void TimeConsumingOperation1()
    {
        Thread.Sleep(TimeSpan.FromSeconds(8));
        ResultOfTimeConsumingOperation = "Hooray we finished the work lol";
        this.closeTask =
            Task.Factory.FromAsync(
                BeginInvoke(new Action(Close)),
                EndInvoke);
    }

    protected override void OnLoad()
    {
        base.OnLoad();
        this.longRunningTask =
            Task.Run(TimeConsumingOperation1);
    }
}

public class MyParentForm : Form
{
    private List<Form> childForms;

    public MyParentForm() : base()
    {
        childForms = new List<Form>();
    }

    protected override void OnLoad()
    {
        base.OnLoad();
        RunChildForm();
    }

    private void RunChildForm()
    {
        var childForm = new MyChildForm();
        childForms = childForms.Append(childForm).ToList();
        childForm.FormClosing += ChidlForm_FormClosing;
        childForm.Show();
    }

    private void ChildForm_FormClosing(object sender, FormClosingEventArgs e)
    {
        var childForm = sender as MyChildForm;
        childForm.FormClosing -= ChildForm_FormClosing;
        if (childForms.Contains(childForm))
            childForms =
                childForms.
                    Except(new Form[] { childForm }).
                    ToList();

       // tada
       myStatusLabel.Text = childForm.ResultOfLongRunningProcess;
    }

    // main window is closing
    protected override void OnFormClosing(FormClosingEventArgs e)
    {
        // let's close any windows we left open
        var localForms = childForms.ToList();
        childForms = new List<Form>();
        foreach (var form in localForms)
            form.Close();
    }
}
于 2021-11-10T18:06:53.473 回答
0

好的,所以在花了一天多的时间试图弄清楚如何使用 Invoke 之后,我意识到虽然在使用线程时这似乎是一种很好的做法,但这不是我的答案。我的问题与关闭处理所有控件的子窗体直接相关,因此它失去了对 MdiParent 的引用。为了解决这个问题,我做了以下事情:

在我的子班中,我添加了:

public static Form IsFormAlreadyOpen(Type FormType)
{
    foreach (Form OpenForm in Application.OpenForms)
    {
    if (OpenForm.GetType() == FormType)
        return OpenForm;
    }
    return null;
 }

我不认为这是最优雅的解决方案,但理论上是当我需要访问 Toolstripstatuslabel 时,我的 Parent 表单将始终打开。所以我基本上遍历所有打开的表单以找到对活动 MdiParent 实例的引用,然后将其传递回调用者。然后在线程中使用以下代码。

 MDIParent1 fm = null;
 if ((fm = (MDIParent1)IsFormAlreadyOpen(typeof(MDIParent1))) != null)
 {
      fm.Toolstripstatuslabel1.Text = "Order No:" + strOrderNo + " was successfully send to " + strEmails;
 }

我仍在寻找更好的方法,但现在这可行。

于 2017-07-06T14:13:36.250 回答