简体   繁体   English

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

[英]BackgroundWorker: Enabling/Disabling Buttons in Completed Event not working after switching between UserControls

I have a problem with my BackgroundWorker placed in a UserControl . 我将BackgroundWorker放在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. 我的WPF应用程序的左侧有一个导航,每个条目都加载自己的UserControl ,用户可以在其中生成PDF文件。

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. 由于创建PDF需要花费一些时间,因此我已经实现了可以完成此工作的BackgroundWorker ,此外,我还禁用了一些按钮并显示进度条。 In the RunWorkerCompleted event I reset the state of the buttons and hide the progressbar. RunWorkerCompleted事件中,我重置了按钮的状态并隐藏了进度条。

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. 尽管有一种情况,所有这些工作都非常好:在PDF创建运行时,用户可以在UserControls之间切换,并且如果他返回开始工作的控件,则控件应显示进度栏和禁用的按钮。 To achieve this I added a variable (isProcessing) to the UserControl . 为此,我向UserControl添加了一个变量(isProcessing)。

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. 但是,如果作业结束并且PDF就绪,则无法启用按钮,并且进度条也保持可见。 The code is placed in the RunWorkerCompleted event: 代码放置在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 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. 如果用户停留在开始作业的控件中,则按钮和进度栏的状态将正确重置。

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. 这将创建BackgroundWorker的实例。 Which has a RunWorkerCompleted event handler that is not associated with the BGW that is actually running. 具有不与实际运行的BGW关联的RunWorkerCompleted事件处理程序。

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. 您的代码中还有另一个错误,当原始的BGW实例完成工作并设置(现在不可见)控件属性时,这应该使ObjectDisposedException导致程序崩溃。 You are forgetting to call Dispose() on the user controls when you switch between them. 您忘了在用户控件之间切换时调用它们的Dispose()。 Not quite sure how that's done in WPF but in winforms that is an unpluggable leak. 不太确定如何在WPF中完成此操作,但在winforms中这是无法插拔的漏洞。

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. BGW实例需要与用户控制实例分开,这样它才能在交换机中幸存下来。 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. 相当痛苦的是,在创建和处理控件时,您必须连接和取消连接事件,您肯定需要重写Dispose()方法,并且不要忘记调用它。 Making the BGW static is defensible if you only allow the job it does to run one-at-a-time. 如果只允许BGW一次运行一次,则使BGW静态是有道理的。 Which should be normal. 这应该是正常的。 Stacking the user controls so you only ever create them once is a Q&D fix. 堆叠用户控件,这样您就只能创建一次,这是一个Q&D修复。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM