user113164
user113164

Reputation: 1507

C#, WPF, Updating the gui without backgroundworkers

I have a program that takes 10-20 seconds to start. I need to show a window with a progress bar when the program starts up. I know BackgroundWorker's are the correct way to do this but I unfortunately don't have the time to rework the whole gui using threads right now. Here is some code I'm trying but it is not working. Can anyone help..?

using System;
using System.Threading;
using System.Windows;

namespace WpfApplication1
{
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();

            Thread t = new Thread(ShowLoadingWindow);
            t.SetApartmentState(ApartmentState.STA);
            t.Priority = ThreadPriority.Highest;
            t.Start();

            DoSomeLongTask();
            keepLooping = false;
        }

        bool keepLooping = true;
        private void ShowLoadingWindow()
        {
            LoadingWindow lw = new LoadingWindow();
            lw.Show();

            while (keepLooping)
            {
                Thread.Sleep(1);
            }

            lw.Close();
        }

        private void DoSomeLongTask()
        {
            for (int i = 0; i < 20000; i++)
            {
                GC.Collect();
            }
        }
    }
}

The loading window is just a bare window with a progress bar. Howcome this doesn't work?

Upvotes: 2

Views: 5320

Answers (3)

Ian
Ian

Reputation: 34489

If you don't want the background worker you need to adjust your code, to do the long task in the new thread.

using System;
using System.Threading;
using System.Windows;

namespace WpfApplication1
{
  public partial class Window1 : Window
  {
      public Window1()
      {
          InitializeComponent();

          Thread t = new Thread(DoSomeLongTask);
          t.Start();

          // switch this with the DoSomeLongTask, so the long task is
          // on the new thread and the UI thread is free.
          ShowLoadingWindow();
    }
  }
}

If you want to then update a progress bar from your "DoSomeLongTask" method then you'll need to ensure that you call invoke. For example:

delegate void ProgressDelegate(int Progress);  
private void UpdateProgress( int  Progress)  
{  
   if (!progressBar1.Dispatcher.CheckAccess())  
   {  
     progressBar1.Value = Progress;  
   }  
   else  
   {  
     ProgressDelegate progressD = new ProgressDelegate(UpdateProgress);  
     progressBar1.Dispatcher.Invoke(progressD, new object[] { Progress });  
   }  
}  

Upvotes: 4

Esben Skov Pedersen
Esben Skov Pedersen

Reputation: 4517

It really is very easy to use backgroundworker.

public partial class Window1 : Window
{
    BackgroundWorker worker = new BackgroundWorker();

    public Window1()
    {
        worker.DoWork += new DoWorkEventHandler(worker_DoWork);
        worker.ReportsProgress = true;
        worker.ProgressChanged += new ProgressChangedEventHandler(update_progress);
    }


    void worker_DoWork(object sender, DoWorkEventArgs e){
        DoSomeLongTask();
        //call worker.ReportProgress to update bar
    }

    void update_progress(object sender, ProgressChangedEventArgs e)
    {
        myscrollbar.Value = e.Value;
    }
}

The keything to keep in mind is to never touch gui stuff from DoWork method. That has to through ProgressChanged/ReportProgress

Upvotes: 5

Heinzi
Heinzi

Reputation: 172220

You're still doing the long task in the main thread. You need to show the loading window in the main thread and do the long task in the background.

Using a BackgroundWorker is really the simplest solution for this:

BackgroundWorker bw;
LoadingWindow lw;

public Window1() {
    bw = new BackgroundWorker();
    lw = new LoadingWindow();

    bw.DoWork += (sender, args) => {
        // do your lengthy stuff here -- this will happen in a separate thread
        ...
    }

    bw.RunWorkerCompleted += (sender, args) => {
        if (args.Error != null)  // if an exception occurred during DoWork,
            MessageBox.Show(args.Error.ToString());  // do your error handling here

        // close your loading window here
        ...
    }

    // open and show your loading window here
    ...

    bw.RunWorkerAsync(); // starts the background worker
}

Upvotes: 10

Related Questions