2

I know this question has been asked several times an I spent all day trying to understand other answers, but since I am very new to C# and WPF nothing helped me so far. I will try to explain my exact problem as much as I can so it will directly help me.

In my MainWindow.xaml I have a progress bar and some button starting a new thread and a long calculation:

<ProgressBar Height="....... Name="progressBar1"/>
<Button Content="Button" Name="button1" Click="button1_Click" />

Now within my MainWindow.xaml.cs:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, RoutedEventArgs e)
    {
        Thread thread = new Thread(new ParameterizedThreadStart(MyLongCalculation));

        ParameterClass myParameters = new ParameterClass();
        thread.Start(myParameters);
    }

    public void MyLongCalculations(object myvalues)
    {
        ParameterClass values = (ParameterClass)myvalues;
        //some calculations
    }
}

public class ParameterClass
{
    //public variables...
}

Now somehow I have to include somethign in my method MyLongCalculations that will keep updating progressBar1. However, I just can't manage to get it working. I know all this is very simple, but unfortunately it is the level I am at the moment on with C# so I hope an answer not too complicated and as detailed as possible would be great.

philkark
  • 2,417
  • 7
  • 38
  • 59
  • 2
    If you read a lot and tried a lot you should include that in your question in detail. Like, what have you read, what have you tried, what was the outcome, how was it not what you wanted. – H.B. Dec 11 '11 at 16:47
  • 1
    Have you tried `Dispatcher.BeginInvoke()`? – Anatolii Gabuza Dec 11 '11 at 22:30
  • 1
    Yes, now I have, I had to read quite a lot first and also looked at the answers. Now it works, both with Dispatcher and BackgroundWorker. – philkark Dec 13 '11 at 01:49

2 Answers2

3

Background worker is well suited for this.

try this:

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

        // Process data
        ProcessDataAsync(new ParameterClass { Value = 20 });
    }

    /// <summary>
    /// Processes data asynchronously
    /// </summary>
    /// <param name="myClass"></param>
    private void ProcessDataAsync(ParameterClass myClass)
    {
        // Background worker
        var myWorker = new BackgroundWorker
        {
            WorkerReportsProgress = true,
        };

        // Do Work
        myWorker.DoWork += delegate(object sender, DoWorkEventArgs e)
        {
            // Set result
            e.Result = MyLongCalculations(myClass);

            // Update progress (50 is just an example percent value out of 100)
            myWorker.ReportProgress(50);
        };

        // Progress Changed
        myWorker.ProgressChanged += delegate(object sender, ProgressChangedEventArgs e)
        {
            myProgressBar.Value = e.ProgressPercentage;
        };

        // Work has been completed
        myWorker.RunWorkerCompleted += delegate(object sender, RunWorkerCompletedEventArgs e)
        {
            // Work completed, you are back in the UI thread.
            TextBox1.Text = (int) e.Result;
        };

        // Run Worker
        myWorker.RunWorkerAsync();
    }

    /// <summary>
    /// Performs calculations
    /// </summary>
    /// <param name="myvalues"></param>
    /// <returns></returns>
    public int MyLongCalculations(ParameterClass myvalues)
    {
        //some calculations
        return (myvalues.Value*2);
    }

}

/// <summary>
/// Custom class
/// </summary>
public class ParameterClass
{
    public int Value { get; set; }
}
Xcalibur37
  • 2,305
  • 1
  • 17
  • 20
  • Thanks a lot, I will try that. However, the method (in this simple case: MyLongCalculation() needs an input from the UI. At the moment I would have probably just created a class that will be used to save all the values since using ParameterizedThreadStart only allows an input of an object. How do I give an input to a BackgroundWorker? May it be from an object using my ParameterClass. Or maybe directly from the UI´s TextBoxes... – philkark Dec 12 '11 at 20:24
  • I updated it with the ability to grab the result and return it to the main thread. RunWorkerCompleted is in the UI thread, so you could add the return value to a UI control at that point without thread contention. – Xcalibur37 Dec 12 '11 at 22:53
  • Is there a way to use the BackgroundWorker and update the UI too? For example a text of a textbox?! – philkark Dec 13 '11 at 00:05
  • Updated to demonstrate updating "TextBox1". Remember, RunWorkerCompleted is back in the UI thread, so you can just call your UI object directly from there. – Xcalibur37 Dec 13 '11 at 00:12
  • One other note: In your other comment you mentioned having long calculations. If you cannot reasonably break down your methods and update the progress at regular intervals, it might be best to set your ProgressBar to Indeterminate. – Xcalibur37 Dec 13 '11 at 00:16
  • Thank you a lot for your help. It is working very well now and I suppose I will break down my calculations into many methods, so it's easier to see the progress... Since this is also more or less part of the topic I will not create a new question, but ask right here, if you don't mind: My next task will be drawing a graph of the values one after another. I guess I will have to create many circles in runtime etc. Since the Backgroundworker works at a speed according to the systems resources, is it possible to somehow synchonize its speed, so lets say the UI updates at say 20fps?! – philkark Dec 13 '11 at 00:49
  • Not sure what graphing method you are going to use. MSChart would be a good route since it's included (http://archive.msdn.microsoft.com/mschart). Really, your best bet for speed is to use DispatcherTimer and update the graph during the Tick event. I have not written it exactly that way before so I do not have a sample. If you are looking for more detail I recommend creating a new thread so the topic is not lost in this one. Also, if you like my solution, please mark it. – Xcalibur37 Dec 13 '11 at 01:10
  • Yes, I really thank you for your help and I think I will use the BackgroundWorker for calculations and a Dispatcher for the graphics then. I don't have a reputation of more than 15 yet (I'm new here), however, I will not forget to vote your Answer up, once I will get there. – philkark Dec 13 '11 at 01:48
  • Thanks. I am happy to help. Come find me through my blog posted in my profile and we can chat if you need more assistance offline. – Xcalibur37 Dec 13 '11 at 01:53
0

You can use Dispatcher.BeginInvoke() to push UI changes on the UI thread rather than worker thread. Most important thing - you need to access Dispatcher which is associated with UI thread not a worker thread you are creating manually. So I would suggest cache Dispatcher.Current and then use in a worker thread, you can do this via ParametersClass or just declaring a dispatchr field on a class level.

public partial class MainWindow
{
    private Dispatcher uiDispatcher;

    public MainWindow()
    {
        InitializeComponents();

        // cache and then use in worker thread method
        this.uiDispatcher = uiDispatcher;
    }

    public void MyLongCalculations(object myvalues)
    {
        ParameterObject values = (ParameterObject)myvalues;
        this.uiDispatcher.BeginInvoke(/*a calculations delegate*/);
    }
}

Also if you need to pass a UI dispatcher in some service/class (like ParametersClass) I would suggest take a look at this nice SO post which show how you can abstract it by an interfaces with ability to push UI changes synchronously/asynchronously so it would be up to a caller (basically use Invoke() or BeginInvoke() to queue a delegate in the UI messages pipeline).

Community
  • 1
  • 1
sll
  • 61,540
  • 22
  • 104
  • 156
  • Thank you. I think to really understand this I will really start learning about Multithreading and Dispatchers. Since I so far only programmed in C, something like that was not really possible and all calculations for me were so far done in the console one after another. It´s quite a big change to suddenly go to object oriented + UI + multithreading =S – philkark Dec 12 '11 at 20:27
  • Okay, I managed to do it, however, I my calculations are VERY long, I would still have to wait until one of the loops are done. I will see how it works. – philkark Dec 12 '11 at 23:51