【问题标题】:Background worker is not reporting progress Winforms后台工作人员未报告进度 Winforms
【发布时间】:2022-06-11 08:42:17
【问题描述】:

我有一个后台工作程序正在运行,它正在从 xml 文件动态生成表单字段。根据 xml 的大小,加载需要一些时间,所以我使用加载栏来报告要使用的进度,这样它们就不会退出程序。该程序按预期工作,它隐藏加载面板并在工作人员完成时显示表单字段,但在加载时,加载栏不会加载。我没有收到任何错误。

这是调用报告进度的地方:

                if (!retrievePath.Equals(""))
                {
                    // create the template with the data from the file
                    XDocument filledDoc = templateCreator.CreateTemplateWithGivenData2(retrievePath, fileName2);
                    tempDoc = filledDoc;
                    XElement root = tempDoc.Root;
                    // get child forms of return data state and sections
                    IDataInterface dataInterface = new DataInterfaceImplementation();
                    IEnumerable<XElement> sections = dataInterface.GetSections(filledDoc);
                    // Grab forms that aren't empty
                    IEnumerable<XElement> forms = XmlClass.GetMefForms(filledDoc).Where(u => u.Value != "").ToList();
                    IEnumerable<XElement> extra = dataInterface.GetSections(filledDoc).Where(u => u.Value != "").ToList();
                    // get the return header state
                    elemForms = dataMiddleman.GetSections(filledDoc);

                    foreach (XElement el in elemForms)
                    {
                        if (el.Name.LocalName.Equals("ReturnHeaderState"))
                        {
                            createForms(el, 3);
                        }
                    }
                    foreach (XElement el in forms)
                    {
                        i = i + 1;
                        i = (i / forms.Count()) * 100;
                        if (i == 100)
                        {
                            i = (i / (forms.Count() - 1)) * 100;
                        }
                        createForms(el, i);
                    }
        private void createForms(XElement x, int i)
    {
        this.Invoke((MethodInvoker)delegate {
            backgroundWorker1.ReportProgress(i);
            var pLabel = new ParentLabel(x);
            this.leftGroup.Controls.Add(pLabel);
            var parentPanel = new CustomPanel(x);
            parentPanel.SendToBack();
            this.thebox.Controls.Add(parentPanel);
            RecursiveTraverse(x, parentPanel);
            pLabel.Click += (sender, e) => PLabel_Click(sender, e);
            pPanels.Add(parentPanel);
        });
    }

这是我的后台工作代码:

    private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
    {
        loadingPanel.BringToFront();
        populateNewFields();
    }
    private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        loadingBar.Value = e.ProgressPercentage;
    }
    private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        loadingBar.Value = 100;
        Thread.Sleep(100);
        loadingPanel.SendToBack();
        loadingBar.Value = 0;
    }

【问题讨论】:

  • 为什么要在委托中调用 BackGroundWorked1 控件`this.Invoke((MethodInvoker)delegate {backgroundWorker1.ReportProgress(i);
  • 一旦后台执行进行到 createForms() 调用并且代码开始调用 ReportProgress(),UI 线程正在燃烧 100% 内核。没有有意义的方法来衡量进度并不少见,然后您必须求助于 Style = Marquee。
  • 你设置了 WorkerReportsProgress = true 吗?

标签: c# .net winforms


【解决方案1】:

您的问题是关于 Background worker is not reporting progress Winforms,我希望我可以使用 Minimal Reproductible Example 来演示如何在后台线程上发生进度时正确触发 event (这是实现您想要的结果的一种方式)。

这个Form 将提供一种使用MockCreateForm 方法测试通知的方法,该方法通过阻塞后台工作程序5 毫秒来模拟表单创建。我相信您的设计规范是每 100 次操作发送一次通知。

通用event 缺少所需的属性,因此继承EventArgs 以自定义接收到的信息。

public delegate void ProgressEventHandler(ProgressEventArgs e);
public class ProgressEventArgs : EventArgs
{
    public ProgressEventArgs(int count, int total)
    {
        Count = count;
        Total = total;
    }
    public int Count { get; }
    public int Total { get; }
}

当按钮状态被切换时,它会使用CancellationToken 调用此工作程序Task,因此它可以被暂停。每 100 次,Progress event 就会被触发:

private void btnWorker_CheckedChanged(object sender, EventArgs e)
{
    if(btnWorker.Checked)
    {
        _cts = new CancellationTokenSource();
        Task.Run(() => 
        {
            var formCount = 10000;
            for (int i = 0; i < 10000; i++)
            {
                if(_cts.IsCancellationRequested)
                {
                    return;
                }
                // Notify every 100 times.
                if((i % 100) == 0)
                {
                    Progress?.Invoke(new ProgressEventArgs(count: i, total: formCount));
                }
                MockCreateForm();
            }
            Progress?.Invoke(new ProgressEventArgs(count: formCount, total: formCount));
        }, _cts.Token);
    }
    else
    {
        _cts.Cancel();
        labelStatus.Text = "Idle";
    }
}
CancellationTokenSource _cts = null;

唯一剩下的就是消费MainForm 中的事件。 唯一需要编组回 UI 线程的是 Label.Text 正在更新的短暂时刻。

public MainForm()
{
    InitializeComponent();
    Progress += (e) =>
    {
        Invoke((MethodInvoker)delegate 
        {
            labelStatus.Text = $"{e.Count} of {e.Total}";
        });
    };
}

您想在后台线程上做什么并不重要。把它放在这里:

public void MockCreateForm()
{
    Task.Delay(5).Wait();
}

我希望这能让你更接近你想要实现的目标。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-10-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多