0

I have a method which is a time consuming one and therefore I have been trying to implement a BackgroundWorker, but it does not allow accessing UI controls which I have read and tried (hacking it) but to no avail.

What my method does: Creates a new BitmapImage, sets the source local or streamed (the parameter), writes it to a new WriteableBitmap, which is used for ConvertToGrayscale and then saves the BW Copy to IsolatedStorage in a folder.

So all this happens quite fast. But, only when I have say less than 25 Source Images. If I have about 100+ Images, this takes considerably long like 20 seconds or more and therefore, I would like to show a ProgressBar in the same PhoneApplicationPage but I have been struggling with how to not block the UI and show the ProgressBar while the method is doing its work.

This is the code that I have:

void GetImages()
    {

        if (!myIsolatedStorage.DirectoryExists("ImagesBW") && !_appsettings.Contains("_update"))
        {

            myIsolatedStorage.CreateDirectory("ImagesBW ");
            for (int i = 0; i < coll.Desserts.Count; i++)
            {
                BitmapImage bmp = new BitmapImage();
                bmp.CreateOptions = BitmapCreateOptions.None;

                if (coll.Desserts[i].HasAssociatedImage)
                {
                    bmp.SetSource(coll.Desserts[i].GetImage());
                    WriteableBitmap wb = new WriteableBitmap(bmp);
                    ConvertToGrayscale(wb);
                    BitmapImage bit = ConvertWBtoBI(wb);
                    SaveBWCopy(bi, i.ToString());
                }
                else
                {
                    bmp.UriSource = new Uri("/Assets/Images/MissingArt.png", UriKind.Relative);
                    WriteableBitmap wb = new WriteableBitmap(bmp);
                    ConvertToGrayscale(wb);
                    BitmapImage bit = ConvertWBtoBI(wb);
                    SaveBWCopy(bi, i.ToString());
                }

            }

            _appsettings["_firstLaunch"] = "false";
            _appsettings.Save();

        }

        else if (myIsolatedStorage.DirectoryExists("ImagesBW ") && _appsettings.Contains("_update"))
        {
            string[] files = myIsolatedStorage.GetFileNames("ImagesBW/*");

            for (int s = 0; s < files.Length; s++)
            {
                myIsolatedStorage.DeleteFile("ImagesBW/" + s + ".jpg");
            }

            myIsolatedStorage.DeleteDirectory("ImagesBW");
            myIsolatedStorage.CreateDirectory("ImagesBW");

                            for (int i = 0; i < coll.Desserts.Count; i++)
            {
                BitmapImage bmp = new BitmapImage();
                bmp.CreateOptions = BitmapCreateOptions.None;

                if (coll.Desserts[i].HasAssociatedImage)
                {
                    bmp.SetSource(coll.Desserts[i].GetImage());
                    WriteableBitmap wb = new WriteableBitmap(bmp);
                    ConvertToGrayscale(wb);
                    BitmapImage bit = ConvertWBtoBI(wb);
                    SaveBWCopy(bi, i.ToString());
                }
                else
                {
                    bmp.UriSource = new Uri("/Assets/Images/MissingArt.png", UriKind.Relative);
                    WriteableBitmap wb = new WriteableBitmap(bmp);
                    ConvertToGrayscale(wb);
                    BitmapImage bit = ConvertWBtoBI(wb);
                    SaveBWCopy(bi, i.ToString());
                }

            }

            _appsettings.Remove("_update");
            _appsettings.Save();

        }

        btnStart.IsEnabled = true;
    }
  • There should be `Invoke` method somewhere. In wpf it's `Dispatcher.Invoke`. You have to periodically block your background processing running as `Task` or `Thread` (or `BackgroundWorker`) when you need to access UI. Best case is if you can acquire everything at once (at beginning and/or at the end), then simply do work without need to invoke (with periodic status reports if you need). – Sinatr Oct 22 '14 at 15:23
  • Hello sorry if I'm being too demading, but could you please elaborate more specifically referring to my scenario, so I can better understand? –  Oct 22 '14 at 15:25

1 Answers1

-1

Backgroundorker is your best bet. There are huge amount of resources on the web how to implement it. But the idea behind is that Backgroundworker runs in a separate thread from the UI. You can access main thread UI by two methods, a delegate or through the Backgroundworker's ProgressChanged method, which gives access to you UI thread.

Lets say you have this.

    public MainWindow()
    {
        InitializeComponent();

       //if you want to disable you button you can do it here 

        BackgroundWorker _bw = new BackgroundWorker();
        _bw.DoWork += new DoWorkEventHandler(_bw_DoWork);
        _bw.WorkerReportsProgress = true;
        _bw.ProgressChanged += new ProgressChangedEventHandler(_bw_ProgressChanged);
        _bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(_bw_RunWorkerCompleted);
        _bw.RunWorkerAsync();

       //or here
       //Display progress bar here too

    }

    void _bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        //this can give you access to the UI after work is completed
        // to check that everything is ok  or hide progress bar
    }

    void _bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
       //this will give you access to the UI in the middle of you work like update progress bar
    }

    void _bw_DoWork(object sender, DoWorkEventArgs e)
    {
        //actual work here including GetImages
        //work work work
       GetImages();

    }

In you getimages method after the SaveBWCopy you can add this to update the progress bar

        _bw.ReportProgress(int progress ) 
        //progress is the percentage you want to send to progress bar to display that is going to  be in the e eventargument you passed. 
KonB
  • 220
  • 1
  • 10
  • But as you can see in my Mehod, it involves mostly accessing UI Controls (BitmapImage, WriteableBitmap). So how do you suggest I breakdown my method to be incorporated into a BackgroundWorker? –  Oct 22 '14 at 15:22
  • Not really, what other UI controls are you accessing besided BI and WB and btnStart? – KonB Oct 22 '14 at 15:31
  • But those are the ones being used the most = Initializing instances, Converting to Grayscale, Converting back to BitmapImage..that's the part which accounts for the most time taken. –  Oct 22 '14 at 15:32
  • I am going to edit my answer to help you out with code. None of those look like actual UI controls. with exception of the button. But you want to add progress bar, which will be UI control – KonB Oct 22 '14 at 15:41
  • Hi, thanks for helping me with the code, but what I've been trying to tell you is that the GetImages() is the one that is time consuming! –  Oct 22 '14 at 16:37
  • Understood, putting GetImages() in the background thread will free up the main ui to do other stuff, like display progress bar and anything else, that code would go after the doasynchwork call. Unless some stuff inside of the GetImages() you want to separate in the background, then you have to be a bit clearer and I can help you there as well. – KonB Oct 23 '14 at 19:51