Example #1
0
        // out parameters can't be used in anonymous methods, so a separate pointer to backgroundWorker is required for return to the caller
        private bool TryBeginImageFolderLoad(string imageSetFolderPath, string selectedFolderPath)
        {
            List <FileInfo> filesToAdd   = new List <FileInfo>();
            List <string>   filesSkipped = new List <string>();

            // Generate FileInfo list for every single image / video file in the folder path (including subfolders). These become the files to add to the database
            // PERFORMANCE - takes modest but noticable time to do if there are a huge number of files.
            // TO DO: PUT THIS IN THE SHOW PROGRESS LOOP
            Util.FilesFolders.GetAllImageAndVideoFilesInFolderAndSubfolders(selectedFolderPath, filesToAdd);

            if (filesToAdd.Count == 0)
            {
                // No images were found in the root folder or subfolders, so there is nothing to do
                Dialogs.ImageSetLoadingNoImagesOrVideosWereFoundDialog(this, selectedFolderPath);
                return(false);
            }
            if (this.State.MetadataAskOnLoad)
            {
                Cursor cursor = Mouse.OverrideCursor;
                PopulateFieldsWithMetadataOnLoad populateField = new PopulateFieldsWithMetadataOnLoad(this, this.DataHandler.FileDatabase, filesToAdd[0].FullName);
                if (this.ShowDialogAndCheckIfChangesWereMade(populateField))
                {
                    this.State.MetadataOnLoad = populateField.MetadataOnLoad;
                }
                Mouse.OverrideCursor = cursor;
            }

            // Load all the files (matching allowable file types) found in the folder
            // Show image previews of the files to the user as they are individually loaded
            // Generally, Background worker examines each image, and extracts data from it which it stores in a data structure, which in turn is used to compose bulk database inserts.
            // PERFORMANCE This is likely the place that the best performance increases can be gained by transforming its foreach loop into a Parallel.ForEach.
            // Indeed, you will see commented out remnants of a Parallel.ForEach in the code where this was done, but using it introduced errors.
            //#pragma warning disable CA2000 // Dispose objects before losing scope. Reason: Not required as Dispose on BackgroundWorker doesn't do anything
            BackgroundWorker backgroundWorker = new BackgroundWorker()
            {
                WorkerReportsProgress = true
            };
            //#pragma warning restore CA2000 // Dispose objects before losing scope

            // folderLoadProgress contains data to be used to provide feedback on the folder loading state
            FolderLoadProgress folderLoadProgress = new FolderLoadProgress(filesToAdd.Count)
            {
                TotalPasses = 2,
                CurrentPass = 1
            };

            backgroundWorker.DoWork += (ow, ea) =>
            {
                ImageSetLoader loader = new ImageSetLoader(imageSetFolderPath, filesToAdd, this.DataHandler);

                backgroundWorker.ReportProgress(0, folderLoadProgress);

                // If the DoWork delegate is async, this is considered finished before the actual image set is loaded.
                // Instead of an async DoWork and an await here, wait for the loading to finish.
                loader.LoadAsync(backgroundWorker.ReportProgress, folderLoadProgress, 500).Wait();
                filesSkipped = loader.ImagesSkippedAsFilePathTooLong;
                backgroundWorker.ReportProgress(0, folderLoadProgress);
            };

            backgroundWorker.ProgressChanged += (o, ea) =>
            {
                // this gets called on the UI thread
                this.ImageSetPane.IsActive = true;

                if (filesSkipped.Count > 0)
                {
                    Dialogs.FilePathTooLongDialog(this, filesSkipped);
                }
                if (folderLoadProgress.CurrentPass == 1 && folderLoadProgress.CurrentFile == 0)
                {
                    // skip the 0th file of the 1st pass, as there is not really much of interest to show
                    return;
                }
                string message = (folderLoadProgress.TotalPasses > 1) ? String.Format("Pass {0}/{1}{2}", folderLoadProgress.CurrentPass, folderLoadProgress.TotalPasses, Environment.NewLine) : String.Empty;
                if (folderLoadProgress.CurrentPass == 1 && folderLoadProgress.CurrentFile == folderLoadProgress.TotalFiles)
                {
                    message = String.Format("{0}Finalizing analysis of {1} files - could take several minutes ", message, folderLoadProgress.TotalFiles);
                }
                else
                {
                    string what = (folderLoadProgress.CurrentPass == 1) ? "Analyzing file" : "Adding files to database";
                    message = (folderLoadProgress.CurrentPass == 2 && folderLoadProgress.CurrentFile == 0)
                        ? String.Format("{0}{1} ...", message, what)
                        : String.Format("{0}{1} {2} of {3} ({4})", message, what, folderLoadProgress.CurrentFile, folderLoadProgress.TotalFiles, folderLoadProgress.CurrentFileName);
                }

                this.UpdateFolderLoadProgress(this.BusyCancelIndicator, folderLoadProgress.BitmapSource, ea.ProgressPercentage, message, false, false);
                this.StatusBar.SetCurrentFile(folderLoadProgress.CurrentFile);
                this.StatusBar.SetCount(folderLoadProgress.TotalFiles);
            };

            backgroundWorker.RunWorkerCompleted += async(o, ea) =>
            {
                // BackgroundWorker aborts execution on an exception and transfers it to completion for handling
                // If something went wrong rethrow the error so the user knows there's a problem.  Otherwise what would happen is either
                //  1) some or all of the folder load file scan progress displays but no files get added to the database as the insert is skipped
                //  2) only some of the files get inserted and the rest are silently dropped
                // Both of these outcomes result in quite poor user experience and are best avoided.
                if (ea.Error != null)
                {
                    throw new FileLoadException("Folder loading failed unexpectedly.  See inner exception for details.", ea.Error);
                }

                // Create an index on RelativePath, File,and RelativePath/File if it doesn't already exist
                this.DataHandler.FileDatabase.IndexCreateForFileAndRelativePathIfNotExists();

                // Show the file slider
                this.FileNavigatorSlider.Visibility = Visibility.Visible;

                await this.OnFolderLoadingCompleteAsync(true).ConfigureAwait(true);

                // Do some final things
                // Note that if the magnifier is enabled, we temporarily hide so it doesn't appear in the background
                bool saveMagnifierState = this.MarkableCanvas.MagnifiersEnabled;
                this.MarkableCanvas.MagnifiersEnabled = false;
                this.StatusBar.SetMessage(folderLoadProgress.TotalFiles + " files are now loaded");
                this.MarkableCanvas.MagnifiersEnabled = saveMagnifierState;

                // If we want to import old data from the ImageData.xml file, we can do it here...
                // Check to see if there is an ImageData.xml file in here. If there is, ask the user
                // if we want to load the data from that...
                if (File.Exists(Path.Combine(this.FolderPath, Constant.File.XmlDataFileName)))
                {
                    ImportImageSetXmlFile importLegacyXmlDialog = new ImportImageSetXmlFile(this);
                    bool?dialogResult = importLegacyXmlDialog.ShowDialog();
                    if (dialogResult == true)
                    {
                        Tuple <int, int> SuccessSkippedFileCounter = ImageDataXml.Read(Path.Combine(this.FolderPath, Constant.File.XmlDataFileName), this.DataHandler.FileDatabase);
                        await this.FilesSelectAndShowAsync(this.DataHandler.FileDatabase.ImageSet.MostRecentFileID, this.DataHandler.FileDatabase.ImageSet.FileSelection).ConfigureAwait(true); // to regenerate the controls and markers for this image

                        Dialogs.ImageSetLoadingDataImportedFromOldXMLFileDialog(this, SuccessSkippedFileCounter.Item1, SuccessSkippedFileCounter.Item2);
                    }
                }

                // Stop the ExifToolManager if it was invoked while loading files, which can occurs when populating metadata to a file via the EXIFTool on load.
                this.State.ExifToolManager.Stop();

                this.BusyCancelIndicator.IsBusy = false; // Hide the busy indicator
            };

            // Set up the user interface to show feedback
            this.BusyCancelIndicator.IsBusy = true; // Display the busy indicator

            this.FileNavigatorSlider.Visibility = Visibility.Collapsed;
            // First feedback message
            this.UpdateFolderLoadProgress(GlobalReferences.BusyCancelIndicator, null, 0, String.Format("Initializing...{0}Analyzing and loading {1} files ", Environment.NewLine, filesToAdd.Count), false, false);
            this.StatusBar.SetMessage("Loading folders...");
            backgroundWorker.RunWorkerAsync();
            return(true);
        }
Example #2
0
        internal async Task LoadAsync(Action <int, FolderLoadProgress> reportProgress, FolderLoadProgress folderLoadProgress, int progressIntervalMilliseconds)
        {
            this.pass1.Start();

            Timer t = new Timer((state) =>
            {
                if (this.LastLoadComplete != null)
                {
                    if (this.LastLoadComplete.File.IsVideo)
                    {
                        folderLoadProgress.BitmapSource = Constant.ImageValues.BlankVideo.Value;
                    }
                    else
                    {
                        folderLoadProgress.BitmapSource = this.LastLoadComplete.BitmapSource;
                    }
                }
                else
                {
                    folderLoadProgress.BitmapSource = null;
                }

                folderLoadProgress.CurrentFile     = this.ImagesLoaded;
                folderLoadProgress.CurrentFileName = this.LastLoadComplete?.File.File;
                int percentProgress = (int)(100.0 * this.ImagesLoaded / this.ImagesToLoad);
                reportProgress(percentProgress, folderLoadProgress);
            }, null, 0, progressIntervalMilliseconds);

            await this.pass1.ConfigureAwait(false);

            t.Change(-1, -1);
            t.Dispose();

            folderLoadProgress.CurrentPass = 2;

            this.pass2.Start();

            t = new Timer((state) =>
            {
                folderLoadProgress.BitmapSource    = null;
                folderLoadProgress.CurrentFile     = this.LastIndexInsertComplete;
                folderLoadProgress.CurrentFileName = this.LastInsertComplete?.File;
                int percentProgress = (int)(100.0 * folderLoadProgress.CurrentFile / this.imagesToInsert);
                reportProgress(percentProgress, folderLoadProgress);
            }, null, 0, progressIntervalMilliseconds);

            await this.pass2.ConfigureAwait(false);

            t.Change(-1, -1);
            t.Dispose();
        }