简体   繁体   中英

BackgroundWorker_ProgressChanged Invoked From Another Class Not Updating Progress Bar

UPDATED With Improved Detail Source Code

Context: I am building a Winforms application using an MVC architecture. My View contains a Search Button, a ProgressBar, and BackgroundWorker control.

Events

        this.wrkBackgroundSearch.WorkerReportsProgress = true;
        this.wrkBackgroundSearch.WorkerSupportsCancellation = true;
        this.wrkBackgroundSearch.DoWork += new System.ComponentModel.DoWorkEventHandler(this.wrkBackgroundSearch_DoWork);
        this.wrkBackgroundSearch.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.wrkBackgroundSearch_RunWorkerCompleted);
        this.wrkBackgroundSearch.ProgressChanged += new System.ComponentModel.ProgressChangedEventHandler(this.wrkBackgroundSearch_ProgressChanged);

The BackgroundWorker (wrkBackgrounSearch)'s DoWork event:

private void wrkBackgroundSearch_DoWork(object sender, DoWorkEventArgs e)
{
    BatchReaderController backgroundCnt = new BatchReaderController();
        BackgroundWorker worker = sender as BackgroundWorker;

        IDictionary<int, object> args = (IDictionary<int, object>)e.Argument;
        //Breaking the arg list down
        Int32 docType                       = (Int32)args[docTypeArgKey];
        Int32 chosenSearchElement           = (Int32)args[searchElementArgKey];
        Int32 environment                   = (Int32)args[environmentArgKey];

        try
        {
            e.Result = backgroundCnt.PerformSearch(docType, chosenSearchElement, environment, criteria, isFilenameSearch, worker, totalFileCount, directoriesToSearch, fileNameMask, xPath);
        }
        catch (Exception ex)
        {
            this.ShowError(ex.Message, "Error while searching", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }
}

I also have this event:

private void wrkBackgroundSearch_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    prgSearchProgress.Value = e.ProgressPercentage;
}

Finally, inside the custom BatchReaderController object's PerformSearch method I have PerformXPathSearch method. (This is the I/O-intensive operation that runs in the background, and is supposed to provide async updates.)

private IList<BatchFileData> PerformXPathSearch(IList<String> uncPaths, Int32 docType, Int32 searchElement, String searchCriteria, BackgroundWorker worker, Int64 totalFileCount, String inputFileMask, String inputXPathQuery)
    {
        Int64 numberFilesSearched = 0;
        IList<BatchFileData> searchResults = new List<BatchFileData>();

        //Validate inputs

        //Look in each drive that was input
        foreach (String networkPath in uncPaths)
        {
            DirectoryInfo dir;
            FileInfo[] files = null;
            try
            {
                dir = new DirectoryInfo(networkPath);
            }
            catch (Exception ae)
            {
                throw new Exception("Bad directory path: " + ae.Message, ae);
            }


            if (!dir.Exists)
            {
                continue;
            }

            try
            {
                files = dir.GetFiles(inputFileMask, SearchOption.TopDirectoryOnly);
            }
            catch (Exception ae)
            {
                throw new Exception("Invalid filename mask: " + filenameMask, ae);
            }

            foreach (FileInfo file in files)
            {
                numberFilesSearched++;
                Boolean shouldOpenFile = DetermineWhetherToOpenFile(file.CreationTime);
                XmlDocument xmlDoc;
                XPathNavigator nav;
                XPathNavigator searchNode;
                DataSet xmlAsDataSet;

                if (!shouldOpenFile)
                {
                    Int32 percentComplete = CalculatePercentComplete(totalFileCount, numberFilesSearched);
                    worker.ReportProgress(percentComplete, searchResults.Count);
                    continue;
                }

                try
                {
                    using (FileStream fs = file.OpenRead())
                    {
                        xmlDoc = new XmlDocument();
                        xmlDoc.Load(fs);
                    }
                }
                catch (UnauthorizedAccessException uae)
                {
                    throw new UnauthorizedAccessException("Unable to read path to file: " + file.FullName, uae);
                }
                nav = xmlDoc.CreateNavigator();

                try
                {
                    searchNode = nav.SelectSingleNode(inputXPathQuery);
                }
                catch (ArgumentException ae)
                {
                    throw new ArgumentException("Argument Exception performing node select: " + xpathQuery, ae);
                }
                catch (XPathException xpe)
                {
                    throw new XPathException("Xpath error while performing node select: " + xpathQuery, xpe);
                }

                //If search results returns criteria success, add this data set to the list for display.
                if (searchNode != null)
                {
                    //Capture data for later processing
                }

                Int32 percentageComplete = CalculatePercentComplete(totalFileCount, numberFilesSearched);
                worker.ReportProgress(percentageComplete, searchResults.Count);
            }
        }

        return searchResults;
    }

Calculate Percent Complete

private Int32 CalculatePercentComplete(Int64 totalFileCount, Int64 numFoldersSearched)
    {
        Int32 percentAsInt;
        float searchPercent;

        searchPercent = numFoldersSearched / totalFileCount;

        try
        {
            percentAsInt = Convert.ToInt32(searchPercent);
        }
        catch (OverflowException oe)
        {
            throw new OverflowException("Error getting percent complete: " + oe.Message, oe);
        }

        if ((percentAsInt < 0) || (percentAsInt > 100))
        {
            throw new ArithmeticException("Invalid percentage: " + percentAsInt);
        }

        return percentAsInt;
    }

Problem: When testing, my ProgressBar does not receive the updates, even though while stepping through the code, the ProgressChanged event IS being fired.

I have also tried several iterations of the following, after checking threads on StackOverflow, within the ProgressChanged event:

private void wrkBackgroundSearch_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    prgSearchProgress.Invoke(new PerformProgrssUpdate(this.DisplaySearchProgress),
        new object[]{e.ProgressPercentage});
    //if (prgSearchProgress.InvokeRequired)
    //{
    //    prgSearchProgress.Invoke();
    //}
}

private void DisplaySearchProgress(Int32 percentComplete)
{
    prgSearchProgress.Value = percentComplete;
}

public delegate void PerformProgrssUpdate(Int32 percentComplete);

Note the commented-out attempt of another solution. The search operation will complete successfully, and when the search operation is completed, the ProgressBar control has its value updated to (1/n)% complete, as the search finishes.

Question How do I make this work, such that when my Controller performs the I/O-intense search, the Progress Bar in my view is updated appropriately, so that the user knows some function is being executed?

It's a classic:

Int32 CalculatePercentComplete(Int64 totalFileCount, Int64 numFoldersSearched)
{
  Int32 percentAsInt;
  float searchPercent;

  searchPercent = numFoldersSearched / totalFileCount;
  // searchPercent will be 0.0 here as long as numFoldersSearched < totalFileCount

  ...
}

The fact that searchPercent is a float does not change that numFoldersSearched/totalFileCount is an integer division.

5L / 6L == 0L

The solution to my problem lay in how I was determining the Percent Complete for the Worker's ProgressChanged event. Below is the final solution:

private Int32 CalculatePercentComplete(Int64 totalFileCount, Int64 numFoldersSearched)
    {
        Int32 percentAsInt;
        Decimal searchPercent;

        searchPercent = (Decimal)numFoldersSearched / (Decimal)totalFileCount;

        try
        {
            Decimal x = Math.Round(searchPercent*100, 0, MidpointRounding.ToEven);
            percentAsInt = Convert.ToInt32(x);
        }
        catch (OverflowException oe)
        {
            throw new OverflowException("Error getting percent complete: " + oe.Message, oe);
        }

        if ((percentAsInt < 0) || (percentAsInt > 100))
        {
            throw new ArithmeticException("Invalid percentage: " + percentAsInt);
        }

        return percentAsInt;
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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