首页 > 解决方案 > 成功运行BackgroundWorker() 后程序的其余部分不执行

问题描述

我正在使用 BackgroundWorker() 在后台执行长时间运行的查询,同时显示我的执行正在运行的弹出窗口。

这是我如何调用 bg_worker()

using System.Windows;
using System.ComponentModel;
using System.Threading;
using System;
using System.IO;
using System.Data.SqlClient;
using System.Windows.Input;

namespace TestEnvironment
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>

    public partial class ProgressBarTemplate : Window
    {
        private CreateProjectScreen _CreateProjectScreen;
        private LoginScreen _LoginScreen;

        public ProgressBarTemplate()
        {
            InitializeComponent();
        }

        public static int RunCalculationsMethod(string connectionstring, string foldername)
        {
            bool exists = Directory.Exists(foldername);

            if (!exists)
            {
                Directory.CreateDirectory(foldername);
            }

            try
            {
                using (SqlConnection sqlConnection = new SqlConnection(connectionstring))
                {
                    var calculations_query = "SELECT * FROM table1");

                    using SqlCommand sqlCommand = new SqlCommand(calculations_query, sqlConnection);

                    sqlConnection.Open();

                    sqlCommand.CommandTimeout = 60 * 10;

                    int NumbderOfRecords = sqlCommand.ExecuteNonQuery();

                    return NumbderOfRecords;
                }
            }

            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString(), "Error", MessageBoxButton.OK, MessageBoxImage.Error);
                return -100;
            }
        }

        private void Window_ContentRendered(object sender, EventArgs e)
        {
            BackgroundWorker worker = new BackgroundWorker();
            
            worker.DoWork += worker_DoWork;
            worker.RunWorkerCompleted += BackgroundWorker_RunWorkerCompleted;
            worker.RunWorkerAsync();           
        }

        void worker_DoWork(object sender, DoWorkEventArgs e)
        {
            int IsSuccessful = RunCalculationsMethod("Server=localhost;Database=DB_Name;Integrated Security=SSPI", String.Format("C:\\folder_path\\"));
        }

        void BackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            // This is called on the UI thread when the DoWork method completes
            // so it's a good place to hide busy indicators, or put clean up code

            try
            {
                this.Close();
                MessageBox.Show("DQ Calculations completed successfully", "Information", MessageBoxButton.OK, MessageBoxImage.Information);
            }

            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString(), "Error", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }
    }
}

上面的代码放在一个名为 ProgressBarTemplate() 的窗口中

而我想要的是在按钮单击时调用 background_worker 放置在我的 MainWindow 中的按钮

所以我的 MainWindow 有以下按钮点击

private void RunCalculationsButton_Click(object sender, RoutedEventArgs e)
        {
            //RunCalculationsMethod(SQLServerConnectionDetails(), String.Format("C:\\DQ_Findings_{0}", HomePageTab.Header.ToString().Split(" - ")[1]));
            try
            {
                Application.Current.Dispatcher.Invoke((Action)delegate
                {
                    ProgressBarTemplate win_progressbar = new ProgressBarTemplate();
                    win_progressbar.Show();
                    //RunCalculationsMethod(SQLServerConnectionDetails(), String.Format("C:\\DQ_folder_test\\Findings\\"));
                }); // The code runs up to this point.
                
                //The code below is not executed for a reason, which I am trying to solve with this question
                List<SucessfulCompletion> reportsucessfulcompletion = new List<SucessfulCompletion>();
                reportsucessfulcompletion = SuccessfulCalculationsTimestamp(SQLServerConnectionDetails());

                if (reportsucessfulcompletion[0].Result==1)
                {
                    //Enable is only if successfull
                    PreviewCalculationsButton.IsEnabled = true;
                    PreviewReportButton.IsEnabled = true;

                    //add textbox of sucess
                    TickButtonSymbolCalculations.Visibility = Visibility.Visible;
                    SQLSuccessfulTextCalculations.Visibility = Visibility.Visible;
                    XerrorSymbolCalculations.Visibility = Visibility.Hidden;
                    SQLFailedTextCalculations.Visibility = Visibility.Hidden;
                    SQLSuccessfulTextCalculations.Text = String.Format("Completed On: {0}", reportsucessfulcompletion[0].Timestampvalue);
                }
                else
                {
                    //add textbox of fail
                    TickButtonSymbolCalculations.Visibility = Visibility.Hidden;
                    SQLSuccessfulTextCalculations.Visibility = Visibility.Hidden;
                    XerrorSymbolCalculations.Visibility = Visibility.Visible;
                    SQLFailedTextCalculations.Visibility = Visibility.Visible;
                    SQLFailedTextCalculations.Text = String.Format("Failed On: {0}", reportsucessfulcompletion[0].Timestampvalue);
                }
            }

            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString(), "Error", MessageBoxButton.OK, MessageBoxImage.Error);
                return;
            }
        }

单击按钮时,我通过调用窗口 ProgressBarTemplate() 来启动 bg_worder。尽管在完成任务后代码生成一些文本并启用某些按钮的可见性,但它们并没有被执行。为什么会这样?我错过了什么吗?

标签: c#sql-serverwpf

解决方案


代码有点不清楚,所以我将发布它应该如何完成。

BGW 是自 2012 年以来完全被Progress< T>Task.Run取代的过时类。无需使用 BGW 或何时可用。Invokeasync/await

您可以使用异步事件处理程序,在后台执行任何计算并在后台操作完成后更新 UI,而不是将计算放在进度窗口中。进度表似乎没有报告任何进度,所以只需要显示和隐藏它。代码可以很简单:

private async void RunCalculationsButton_Click(object sender, RoutedEventArgs e)
{
    var win_progressbar = new ProgressBarTemplate();
    win_progressbar.Show();
    try
    {
        //Update the UI
        var results=await Task.Run(()=> RunCalculationsMethod(...));
        //Update the UI
    }
    finally
    {
        win_progressbar.Close();
    }
}

try/finally用于确保即使出现错误也关闭表单。

进度报告

进度报告可通过Progress类和IProgress接口获得。IProgress<T>允许后台任务向实现接口的类发送强类型消息。该Progress<T>实现确保消息在创建它的线程中处理,通常是 UI 线程。

假设这是消息类型:

class MyProgress
{
    public int Percent{get;set;}
    public string Message {get;set;}
} 

RunCalculationsMethod可以修改为接受和使用IProgress<MyProgress>;

public static int RunCalculationsMethod(string connectionstring, string foldername, 
                                        IProgress<MyProgress> progress)
{

    progress.Report(new MyProgress{Percent=0,Message="Starting"};
    ....
    progress.Report(new MyProgress{Percent=100,Message="Finished"};
}

事件处理程序只需要创建一个Progress<MyProgress>并提供一个更新 UI 的方法。假设ProgressBarTemplate有这样一个方法,称为Update(string,int)

private async void RunCalculationsButton_Click(object sender, RoutedEventArgs e)
{
    var win_progressbar = new ProgressBarTemplate();
    IProgress<MyProgress> pg=new Progress<MyProgress>(pg=>
                        win_progressbar.Update(pg.Message,pg.Percent));
    win_progressbar.Show();
    try
    {
        //Update the UI
        var results=await Task.Run(()=> RunCalculationsMethod(...,pg));
        //Update the UI
    }
    finally
    {
        win_progressbar.Close();
    }
}

您可以在 4.5 中的 Async 中找到更详尽的解释:在 Async API 中启用进度和取消


推荐阅读