/// <summary> /// Populates the _flowLayoutPanel with images from the directory selected in the /// _folderBrowserDialog. This method is designed to be called on a background thread. /// </summary> private void PopulateImages() { //TODO: Support ".jpeg" also? Can I do that with a comma or some other syntax e.g. *.jpg,*.jpeg ? string[] filenames = System.IO.Directory.GetFiles(this._folderBrowserDialog.SelectedPath, "*.jpg"); //Set the current directory that we'll use to load and save files System.IO.Directory.SetCurrentDirectory(this._folderBrowserDialog.SelectedPath); //Inform the progress dialog of the count of files that we'll be loading. (This is needed by //the progress dialog to calculate the percentage finished based on the number of the current //file being processed.) Invoke(this._setProgressDialogFileCount, new object[] { filenames.Length }); //Build the list of images that we'll display in the dialog. List <string> errorFiles = new List <string>(); int imageIndex = 1; foreach (string filename in filenames) { //Skip loading the remainder of the files if the Cancel button on the progress meter was clicked. if (this._cancelOperation == true) { break; } //Update the Loading dialog with the number of files loaded so far. Invoke(this._updateProgressDialog, new object[] { imageIndex }); //The height and width of the maximum allowed size of thumbnail images. const int maxThumbnailHeightWidth = 140; const int borderWidth = 6; DualImagePanel innerPanel = new DualImagePanel(maxThumbnailHeightWidth, maxThumbnailHeightWidth); innerPanel.Width = 335; innerPanel.Height = 170; innerPanel.Location = new Point(borderWidth, borderWidth); innerPanel.BackColor = this.BackColor; innerPanel.RotationEvent += new DualImagePanel.RotationDelegate(innerPanel_RotationEvent); //Get the image from the file, create a small (thumbnail) version of the image //that will fit within the containing panel, and put the thumbnail image into //the panel. Then release the original image. (For lots of large images, it //would take too much memory to hold all of the originals in memory.) try { //NOTE: Using Image.FromStream() instead of Image.FromFile() to load images, it is *much* faster! //Thanks to http://www.shahine.com/omar/CommentView,guid,673c131f-26db-4f44-9908-c2667da832ad.aspx using (FileStream fileStream = new FileStream(filename, FileMode.Open, FileAccess.Read)) { using (Image image = Image.FromStream(fileStream, true, false)) { Image thumbnailImage = Utility.ResizeImageToFitRectangle(image, new Size(maxThumbnailHeightWidth, maxThumbnailHeightWidth)); Orientation imageOrientation = Utility.GetImageOrientation(image); innerPanel.SetOriginalImage(thumbnailImage, imageOrientation, filename); } } this._imageCount++; } catch (Exception) { //Note: I've seen an OutOfMemory exception get thrown when trying to read a file with //an extension of .jpg, but that isn't really an image file. Regardless of what went //wrong, just skip the file and go on to the next one. errorFiles.Add(filename); continue; } //For computers with low memory and large pictures: release memory now GC.Collect(); //Add the innerPanel to the form. object[] controlArguments = new object[] { innerPanel }; this.Invoke(this._addItemToFlowLayoutPanel, controlArguments); if (innerPanel.Orientation != Orientation.Initial) { this._rotatedImageCount++; } imageIndex++; } //If there were any images that we couldn't load successfully, display an error message //with the name(s) of the files that failed to load. if (errorFiles.Count > 0) { string errorMessage = "Error: " + errorFiles.Count + " file(s) did not load correctly and were skipped:" + Environment.NewLine + Environment.NewLine; errorMessage += errorFiles[0]; for (int fileIndex = 1; fileIndex < errorFiles.Count; fileIndex++) { //Show a max of 50 failed filenames if (fileIndex > 50) { errorMessage += ", ..."; break; } errorMessage += ", " + errorFiles[fileIndex]; } MessageBox.Show(errorMessage, Utility.APP_NAME, MessageBoxButtons.OK, MessageBoxIcon.Warning); } //Update the status bar text and close the progress dialog. this.Invoke(this._updateStatusText); this.Invoke(this._operationFinished); }
/// <summary> /// Activated by the background thread when the rotated image for the specified panel has /// been saved to disk, replacing the original image on disk. On the specified DualImagePanel, /// sets the original image to be the rotated image, and sets the orientation to Initial. /// </summary> private void SetRotatedImageAsOriginalImage(DualImagePanel panel) { panel.SetRotatedImageAsOriginalImage(); }