BackgroundWorker:在用户控件之间切换后,启用/禁用已完成事件中的按钮不起作用

发布于 2024-09-26 15:51:28 字数 2145 浏览 0 评论 0原文

我的 BackgroundWorker 放置在 UserControl 中时遇到问题。 我的 WPF 应用程序在左侧有一个导航,每个条目都会加载自己的 UserControl,用户可以在其中生成 PDF 文件。

因为创建 PDF 需要一些时间,所以我实现了一个 BackgroundWorker 来完成这项工作,此外我还禁用了一些按钮并显示进度条。 在 RunWorkerCompleted 事件中,我重置了按钮的状态并隐藏了进度条。

尽管有一种情况,所有这些都运行良好: 当 PDF 创建正在运行时,用户可以在用户控件之间切换,如果他返回到开始作业的控件,控件应将进度条和按钮显示为禁用状态。 为了实现这一点,我向 UserControl 添加了一个变量 (isProcessing)。

我有一个控件的构造函数:

        // Check if a current process is running, if so: handle button/progressbar visibility
        if (_isProcessing)
        {
            _stkProgressBar.Visibility = Visibility.Visible;
            progressBar1.IsIndeterminate = true;

            // Disabling the buttons here is just working with a hack in
            // the "Button_IsEnabledChanged" event.
            btnDaten.IsEnabled = false;
            btnBericht.IsEnabled = false;
            this.Cursor = Cursors.Wait;
        }
        else
        {
            _stkProgressBar.Visibility = Visibility.Hidden;
        }

启用/禁用按钮只是因为这个肮脏的黑客而起作用:

    private void btnDaten_IsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e)
    {
        //HACK: We want to disable the report buttons if a report execution is running.
        //      Disabling the buttons in the Load Event of the control isn't working
        if (_isProcessing && (bool)e.NewValue)
        {
            btnDaten.IsEnabled = false;
            btnBericht.IsEnabled = false;
        }
    }

现在,如果作业正在运行并且用户在控件之间切换,则处理控件的状态很好。但是,如果作业结束并且 PDF 准备就绪,则无法启用按钮,并且进度条也保持可见。代码放置在 RunWorkerCompleted 事件中:

    void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        // This is not working if the user switches between the controls
        _isProcessing = false;
        this.Cursor = Cursors.Arrow;
        _stkProgressBar.Visibility = Visibility.Hidden;
        btnDaten.IsEnabled = true;
        btnBericht.IsEnabled = true;
    }

我对其进行了调试,发现按钮获得了正确的输入,因此应该启用,但没有任何反应。如果用户停留在开始作业的控件中,按钮和进度条的状态将正确重置。

I have a problem with my BackgroundWorker placed in a UserControl.
My WPF application has a navigation on the left and each entry loads its own UserControl where the user can generate a PDF file.

Because the creation of the PDF takes some time I've implemented a BackgroundWorker which does the job and additionally I disable some buttons and show a progressbar.
In the RunWorkerCompleted event I reset the state of the buttons and hide the progressbar.

All of this is working very well, despite one scenario:
While the PDF creation is running the user can switch between the UserControls and if he returns to the control where he startet the job, the control should display the progressbar and the buttons as disabled.
To achieve this I added a variable (isProcessing) to the UserControl.

A the constructor of the control I have this:

        // Check if a current process is running, if so: handle button/progressbar visibility
        if (_isProcessing)
        {
            _stkProgressBar.Visibility = Visibility.Visible;
            progressBar1.IsIndeterminate = true;

            // Disabling the buttons here is just working with a hack in
            // the "Button_IsEnabledChanged" event.
            btnDaten.IsEnabled = false;
            btnBericht.IsEnabled = false;
            this.Cursor = Cursors.Wait;
        }
        else
        {
            _stkProgressBar.Visibility = Visibility.Hidden;
        }

Enabling/Disabling the buttons there is just working because of this dirty hack:

    private void btnDaten_IsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e)
    {
        //HACK: We want to disable the report buttons if a report execution is running.
        //      Disabling the buttons in the Load Event of the control isn't working
        if (_isProcessing && (bool)e.NewValue)
        {
            btnDaten.IsEnabled = false;
            btnBericht.IsEnabled = false;
        }
    }

Now if a job is running and the user switches between the control, the state of the processing control is fine. But if the job is over and the PDF ready, the buttons can't get enabled and the progressbar also stays visible. The code is placed in the RunWorkerCompleted event:

    void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        // This is not working if the user switches between the controls
        _isProcessing = false;
        this.Cursor = Cursors.Arrow;
        _stkProgressBar.Visibility = Visibility.Hidden;
        btnDaten.IsEnabled = true;
        btnBericht.IsEnabled = true;
    }

I debugged it and saw that the buttons get the right input and therefore should be enabled, but nothing happens. If the user stays in the control where he startet the job, the state of the buttons and the progressbar is resetted correctly.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

抱着落日 2024-10-03 15:51:29

是的,用户控件之间的切换就是这里的问题。当您切换离开并切换回来时,您将创建该控件的实例。这会创建一个BackgroundWorker的实例。其中有一个 RunWorkerCompleted 事件处理程序,该处理程序与实际运行的 BGW 无关。

您的代码中还有另一个错误,当原始 BGW 实例完成作业并设置(现在不可见)控制属性时,这将使您的程序崩溃并出现 ObjectDisposeException。当您在用户控件之间切换时,您忘记在用户控件上调用 Dispose()。不太确定 WPF 中是如何完成的,但在 winform 中这是一个不可插入的泄漏。

只要您想支持切换,您就必须采取不同的方式。 BGW 实例需要与用户控制实例分开,这样它才能在切换后继续存在。相当痛苦,您必须在创建和处置控件时连接和取消连接事件,您肯定需要重写 Dispose() 方法并且不要忘记调用它。如果您只允许 BGW 执行的作业一次运行一个,那么将 BGW 设为静态是合理的。这应该是正常的。堆叠用户控件以便您只创建一次是 Q&D 修复。

Yes, the switching between user controls is the problem here. When you switch away and switch back, you create a new instance of the control. Which creates a new instance of the BackgroundWorker. Which has a RunWorkerCompleted event handler that is not associated with the BGW that is actually running.

There's another bug in your code, this should crash your program with an ObjectDisposedException when the original BGW instance finishes the job and sets the (now invisible) control properties. You are forgetting to call Dispose() on the user controls when you switch between them. Not quite sure how that's done in WPF but in winforms that is an unpluggable leak.

You are going to have to this differently as long as you want to support switching. The BGW instance needs to be separate from the user control instance so it can survive the switch. Fairly painful, you'll have to wire and unwire the events as the control gets created and disposed, you definitely need to override the Dispose() method and not forget to call it. Making the BGW static is defensible if you only allow the job it does to run one-at-a-time. Which should be normal. Stacking the user controls so you only ever create them once is a Q&D fix.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文