Search code examples
c#wpfbackgroundworkerclipboard

Update progressbar + clipboard.getimage() with backgroundworker - WPF C#


My program should export certain Excel files from a folder as an image (in SVG format) into another folder. This can be done by the Methods CopyAndSaveTo and ExportRangeAsImage and they do their job. On the MainWindow I have a button that performs these two functions when it is clicked. I want the User who clicks on this button be informed (Progressbar) how far is this process (Copy + export). I have tried to realise it using a BackgroundWorker and it works only if I comment the following code (in the Method ExportRangeAsImage):

Bitmap image = new Bitmap(System.Windows.Forms.Clipboard.GetImage());
image.Save(ImagePath + Path.GetFileNameWithoutExtension(file) + ".svg");

Otherwise I get the following error message ( translated from German):

In System.NullReferenceException an exception of type "System.Drawing.dll" has occurred, but was this not processed in the user code. Additional information: Object reference not set to an object instance. If a handler is available for this exception, the program may continue to run safely.

Here is the whole code:

private void UpdateDataOfLine1(object sender, ExecutedRoutedEventArgs e)
        {
            string[] files = Directory.GetFiles(RootPath);
 
            BackgroundWorker worker = new BackgroundWorker();
            worker.WorkerReportsProgress = true;
            worker.DoWork += worker_DoWork;
            worker.ProgressChanged += worker_ProgressChanged;
            worker.RunWorkerAsync();
        }
 

 
        void worker_DoWork(object sender, DoWorkEventArgs e)
        {
            string[] files = Directory.GetFiles(RootPath);
 
            for (int i = 0; i < files.Length; i++)
            {
                if (files[i].Contains("$") || files[i].Contains("~") || files[i].Contains("Thumb"))
                {
                    continue;
                }
 
                File.Copy(files[i], DestPath + Path.GetFileName(files[i]), true);
 
                string newFile = DestPath + Path.GetFileName(files[i]);
 
                ExPortRangeAsImage(newFile);
 
                (sender as BackgroundWorker).ReportProgress(i);
                Thread.Sleep(100);
            }
        }
 

 
        void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            Status.Value = e.ProgressPercentage;
        }
 

        public void ExPortRangeAsImage(string file)
        {
            var ExcelApp = new Microsoft.Office.Interop.Excel.Application();
 
            try
            {
                if (file.Contains("BeispielDatei"))
                {
                    Workbook wb = ExcelApp.Workbooks.Open(file);
                    Worksheet ws = (Worksheet)wb.Sheets[1];
                    Range range = ws.Range["A1:U35"];
                    range.CopyPicture(XlPictureAppearance.xlScreen, XlCopyPictureFormat.xlBitmap);
                    wb.Close(SaveChanges: false);
                    Marshal.ReleaseComObject(wb);
                }
 
                else
                {
                    Workbook wb = ExcelApp.Workbooks.Open(file);
                    Worksheet ws = (Worksheet)wb.Sheets[1];
                    Range range = ws.Range["A1:U35"];
                    range.CopyPicture(XlPictureAppearance.xlScreen, XlCopyPictureFormat.xlBitmap);
                    wb.Close(SaveChanges: false);
                    Marshal.ReleaseComObject(wb);
                }
            }
 
            finally
            {
                Bitmap image = new Bitmap(System.Windows.Forms.Clipboard.GetImage());
                image.Save(ImagePath + Path.GetFileNameWithoutExtension(file) + ".svg");
 
                Marshal.ReleaseComObject(ExcelApp);
            }
        }

Can you show me what I'm doing wrong or how I can realize it? Are there other ways than BackgroundWorker?

Thank you in advance for your help!

Here is a screenshot of the Error


Solution

  • You should review the stacktrace, I think your inner exception is most likely due to cross thread access violation.

    Null reference is probably getting triggered due to: System.Windows.Forms.Clipboard.GetImage() which likely is returning null.

    You can try running this portion in UI dispatcher thread:

    Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(()=>
    {
       Bitmap image = new Bitmap(System.Windows.Forms.Clipboard.GetImage());
       image.Save(ImagePath + Path.GetFileNameWithoutExtension(file) + ".svg");
    }
    

    This will also most likely introduce synchronization issues (race between copy and paste). You may have to restructure your code to ensure image writing is complete before copying another image into clipboard.