1

I need to use a backgroundworker for a progress bar in wpf, and I'm new to C#. I've read a lot about them, and I have seen that I can create a backgroundWorker, and move the logic that needs to be tracked into a DoWork method, but I can't seem to separate the logic. Any help is appreciated.

EDIT This is a problem I'm actually having from a much larger program. I understand that having a background working for multiplying numbers would be a bad idea. This is just the smallest problem I could come up with that shows what issues I'm having./EDIT

The program is just taking two groups of numbers and multiplying them, but I'd like a progress bar in a separate thread that updates the same time as a MessageBox showing my percentage done, and a textBlock being updated with the values.

MainWindow.xaml.cs

namespace backgroundWorkersWPF
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            double[] group1 = { 1, 2, 3, 4 };
            double[] group2 = { 5, 6, 7, 8 };

            double multipliedNumbers = 0;

            double totalCount = group1.Count() * group2.Count();
            double percentageDone = 0;

            double steps = 0;

            foreach (double x in group1)
            {

                foreach (double y in group2)
                {
                    multipliedNumbers = x * y;


                    txtBlock.Text = multipliedNumbers.ToString();

                    steps++;

                    /*I need this part in a separate thread, but I don't know 
how to easily move this log to a DoWork method without moving everything.*/
                    percentageDone = steps / totalCount * 100;
                    progBar.Value = percentageDone;

                    /*So, if the above part were in a separate thread, 
hopefully I would see my MainWindow with a textblock showing the 
current multiplication value, a messagebox that reports a numerical value
for the percentage, and a progress bar that changes percentage every time 
I close the messagebox. */
                    MessageBox.Show(percentageDone.ToString());
                }
            }
        }
    }
}

MainWindow.xaml

<Window x:Class="backgroundWorkersWPF.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <TextBlock Name="txtBlock"/>
        <ProgressBar Height="20"
                     Width="Auto"
                     Minimum="0"
                     Maximum="100"
                     Name="progBar"/>


    </Grid>
</Window>

So to recap, what happens now, is that since everything is on a single thread, my MessageBox's show up, and count up the percentage completes, and then once it hits 100%, it opens the MainWindow and shows a full progress bar, and the textBlock shows the answer to the last two numbers that were multiplied.

What I would like to happen, is have the progress bar run in a separate thread, and everything else stay in the current thread.

I assumed I needed to create a BackgroundWorker, and somehow send the updated percentage complete values to it, but I've only been able to find simple examples of background workers where you move all of your logic into them.

EDIT To clarify, I said that I only want the progress bar in the new thread, but I don't mind if something else, like calculations go in the new thread as well. I just haven't been able to add a background worker without messing up the program royally, or moving ALL of the logic to another thread. I'll add some code to show the types of mistakes I've done in the past, and what I don't want./EDIT

I don't think this is a duplicate because... I don't think this is a duplicate because I'm not asking How to set up a BackgroundWorker, I'm asking how I should separate the logic for it. For example.

I have a nested for loops. It would be simple for me to create a BackgroundWorker and have my DoWork method just have both of the for loops inside of it. But there is logic inside of those loops that I don't want on the separate thread. I'm new to this, so I may be asking the question incorrectly, but I feel as if asking how to separate this logic is a different question than how to use a background worker.

EDIT

I found a solution that works for me. I know this is currently marked as duplicate, but I'm going to add my solution anyway, just in case someone stumbles across this also trying to figure out C# threading.

To answer my question, separating the logic is as simple as creating new functions. I really didn't understand how background workers worked, was my problem. I wasn't sure how to partition out that data I needed in the background. I found my solution while learning more about Async/Await. Async/Await makes more sense to me, so it was easier to understand, and as it turns out, that should be used over BackgroundWorkers in most cases it seems. So here's my solution. Hopefully this gets opened back up eventually.

MainWindow.xaml

<Window x:Class="backgroundWorkersWPF.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <TextBlock Name="txtBlock"/>
        <ProgressBar Height="20"
                     Width="Auto"
                     Minimum="0"
                     Maximum="100"
                     Name="progBar"/>
        <TextBlock Name="percentage"
                   VerticalAlignment="Center"
                   HorizontalAlignment="Center"
                   />


    </Grid>
</Window>

MainWindow.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace backgroundWorkersWPF
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            double[] group1 = { 1, 2, 3, 4 };
            double[] group2 = { 5, 6, 7, 8 };



            //Now the work I wanted is going to an async function.  And the data I need in the main thread is returned using IProgress<>
            doWork(group1, group2);


        }

        private async void doWork(double[] group1, double[] group2)
        {

            double percentageDone = 0;
            double steps = 0;
            double multipliedNumbers = 0;
            double totalCount = group1.Count() * group2.Count();

            foreach (double x in group1)
            {

                foreach (double y in group2)
                {
                    multipliedNumbers = x * y;


                    txtBlock.Text = "Multiplied Numbers: " + x + " * " + y + " = " + multipliedNumbers.ToString();

                    steps++;

                    var progress = new Progress<int>(updateProgressBar);
                    await System.Threading.Tasks.Task.Factory.StartNew(() => calculatePercentage(steps, totalCount, percentageDone, progress));

                }
            }
        }
        private void calculatePercentage(double steps, double totalCount, double percentageDone, IProgress<int> progress)
        {
            percentageDone = steps / totalCount * 100;
            progress.Report((int)percentageDone);
            MessageBox.Show(percentageDone.ToString());

        }
        private void updateProgressBar(int percentageDone)
        {
            progBar.Value = percentageDone;
            percentage.Text = percentageDone.ToString() + "%";
        }
    }
}

/EDIT

trueCamelType
  • 2,198
  • 5
  • 39
  • 76
  • Multiplying a few numbers together will be so blindingly fast that a separate thread is going to create more overhead than it's worth, and the progress bar will go to 100% in microseconds. – Eric J. Nov 02 '15 at 20:53
  • I understand that. This is actually a problem I'm having from a pretty massive program. This is just the smallest recreation of the problem I could do without putting a massive amount of code in here. I should have explained that. I will edit it now. – trueCamelType Nov 02 '15 at 20:54
  • Why move only the percentage part in a separate thread? Doing so will pretty much give u the same result since the calculations are still being done on the UI thread and thus 'blocking' it – kkyr Nov 02 '15 at 21:01
  • I will rephrase my question. What I should have said, is that I need at least the progress bar in a new thread. I just don't know how to make the progress bar and everything else show up at the same time without moving all of the logic into the thread. I'll give an example of what I've done that I know is incorrect. – trueCamelType Nov 02 '15 at 21:03
  • BGW has support for updating the UI with the progress of the background task built right into it. It has several methods for updating the UI with progress, and has examples of how to use them right in the documentation of the type. – Servy Nov 02 '15 at 21:04
  • Anything that is doing intensive work should be moved on a separate thread – kkyr Nov 02 '15 at 21:07
  • @kyriacos_k Long running *CPU bound* work should be done in a non-UI thread. – Servy Nov 02 '15 at 21:12
  • I'll try to fix the question, and provide more code that relevant once I get home. Thanks for the input so far. – trueCamelType Nov 02 '15 at 21:29
  • 1
    @Servy I added some new information. Can you re-evaluate this, and if you still think it's a duplicate, could you give me some hints on how to rephrase the question, or tell me if I should have just given all of the Async/Await information in a new question about partitioning logic? Just trying to learn the SO environment a little better. Like I said, I don't think it's a duplicate, but I also didn't do a great job of asking the question or give enough code originally. – trueCamelType Nov 06 '15 at 16:19

1 Answers1

1

You can update UI elements within a BackgroundWorker by Invoking the main thread. You can do something like this:

            double[] group1 = { 1, 2, 3, 4 };
            double[] group2 = { 5, 6, 7, 8 };

            double multipliedNumbers = 0;

            double totalCount = group1.Count() * group2.Count();
            double percentageDone = 0;

            double steps = 0;


            var bworker = new BackgroundWorker();
            bworker.DoWork += (s, e) =>
            {
                foreach (double x in group1)
                {

                    foreach (double y in group2)
                    {
                        multipliedNumbers = x * y;
                        steps++;

                        this.Invoke((MethodInvoker)delegate()
                        {
                            txtBlock.Text = multipliedNumbers.ToString();
                            percentageDone = steps / totalCount * 100;
                            progBar.Value = percentageDone;
                        });
                    }
                }
            };
            bworker.RunWorkerAsync();
Jonathan Carroll
  • 880
  • 1
  • 5
  • 20