//************************************************************************* // Method: PopulateAreaWithImages() // /// <summary> /// Populates one area of the image column with images. /// </summary> /// /// <param name="oKeyColumnArea"> /// Area from the key column. /// </param> /// /// <param name="sImageColumnName"> /// Name of the image column. /// </param> /// /// <param name="oImageColumnArea"> /// Corresponding area from the image column. /// </param> /// /// <param name="oImageSizePt"> /// Size of each image, in points. /// </param> /// /// <param name="oOldImagesInColumn"> /// A dictionary of zero or more key/value pairs. The key is the /// Shape.Name of an old image in the image column and the value is the /// image, as a Shape. /// </param> /// /// <param name="oTemporaryImages"> /// Contains information about the images that should be inserted. /// </param> /// /// <remarks> /// This method populates <paramref name="oImageColumnArea" /> with the /// temporary images specified by <paramref name="oTemporaryImages" />. /// </remarks> //************************************************************************* private static void PopulateAreaWithImages( Range oKeyColumnArea, Range oImageColumnArea, String sImageColumnName, SizeF oImageSizePt, Dictionary<String, Microsoft.Office.Interop.Excel.Shape> oOldImagesInColumn, TemporaryImages oTemporaryImages ) { Debug.Assert(oKeyColumnArea != null); Debug.Assert(oImageColumnArea != null); Debug.Assert( !String.IsNullOrEmpty(sImageColumnName) ); Debug.Assert(oOldImagesInColumn != null); Debug.Assert(oTemporaryImages != null); // Gather some required information. Int32 iRows = oKeyColumnArea.Rows.Count; Debug.Assert(iRows == oImageColumnArea.Rows.Count); Debug.Assert(oKeyColumnArea.Parent is Worksheet); Worksheet oWorksheet = (Worksheet)oKeyColumnArea.Parent; Microsoft.Office.Interop.Excel.Shapes oShapes = oWorksheet.Shapes; Object [,] aoKeyValues = ExcelUtil.GetRangeValues(oKeyColumnArea); Dictionary<String, String> oFileNames = oTemporaryImages.FileNames; // Set the row heights to fit the images. oKeyColumnArea.RowHeight = oImageSizePt.Height + 2 * ImageMarginPt; // Get the first cell in the image column. Range oImageCell = (Range)oImageColumnArea.Cells[1, 1]; // Loop through the area's rows. for (Int32 iRow = 1; iRow <= iRows; iRow++) { String sKey, sFileName; // Check whether the row's key cell has a corresponding file name // in the dictionary. if ( ExcelUtil.TryGetNonEmptyStringFromCell(aoKeyValues, iRow, 1, out sKey) && oFileNames.TryGetValue(sKey, out sFileName) ) { // Give the picture a name that can be recognized by // GetImagesInColumn(). String sPictureName = sImageColumnName + "-" + sKey; Microsoft.Office.Interop.Excel.Shape oPicture; // If an old version of the picture remains from a previous // call to this method, delete it. if ( oOldImagesInColumn.TryGetValue(sPictureName, out oPicture) ) { oPicture.Delete(); } String sFileNameWithPath = Path.Combine( oTemporaryImages.Folder, sFileName); oPicture = oShapes.AddPicture(sFileNameWithPath, MsoTriState.msoFalse, MsoTriState.msoCTrue, (Single)(Double)oImageCell.Left + ImageMarginPt, (Single)(Double)oImageCell.Top + ImageMarginPt, oImageSizePt.Width, oImageSizePt.Height ); oPicture.Name = sPictureName; } // Move down one cell in the image column. oImageCell = oImageCell.get_Offset(1, 0); } }
//************************************************************************* // Method: PopulateColumnWithImages() // /// <summary> /// Populates an image column in an Excel table (ListObject) with images /// that have been stored in a temporary folder. /// </summary> /// /// <param name="workbook"> /// Workbook containing the table. /// </param> /// /// <param name="worksheetName"> /// Worksheet containing the table. /// </param> /// /// <param name="tableName"> /// Name of the table. /// </param> /// /// <param name="imageColumnName"> /// Name of the image column. The column gets added to the end of the /// table if it doesn't already exist. /// </param> /// /// <param name="keyColumnName"> /// Name of the column containing the keys in the dictionary returned by /// <see cref="TemporaryImages.FileNames" />. For each cell that contains /// a key in the dictionary, an image is inserted into the corresponding /// cell in the image column. /// </param> /// /// <param name="temporaryImages"> /// Contains information about the images that should be inserted. /// </param> /// /// <remarks> /// If a column named <paramref name="imageColumnName" /> doesn't already /// exist, this method adds it to the end of the table. It then populates /// the column with the temporary images specified by <paramref /// name="temporaryImages" />, and deletes the temporary folder containing /// the images. /// /// <para> /// The images are shown by default. Call <see /// cref="ShowOrHideImagesInColumn" /> to hide or reshow them. /// </para> /// /// <para> /// If the specified table doesn't exist, this method does nothing. /// </para> /// /// </remarks> //************************************************************************* public static void PopulateColumnWithImages( Workbook workbook, String worksheetName, String tableName, String imageColumnName, String keyColumnName, TemporaryImages temporaryImages ) { Debug.Assert(workbook != null); Debug.Assert( !String.IsNullOrEmpty(worksheetName) ); Debug.Assert( !String.IsNullOrEmpty(tableName) ); Debug.Assert( !String.IsNullOrEmpty(imageColumnName) ); Debug.Assert( !String.IsNullOrEmpty(keyColumnName) ); Debug.Assert(temporaryImages != null); ListObject oTable; Range oKeyColumnData; // Get the table and the key column data. if ( !ExcelUtil.TryGetTable(workbook, worksheetName, tableName, out oTable) || !ExcelUtil.TryGetTableColumnData(oTable, keyColumnName, out oKeyColumnData) ) { // Nothing can be done without the table or key column. return; } Range oImageColumnData; // Add the image column if it doesn't already exist. if ( !TryGetImageColumnData(oTable, imageColumnName, out oImageColumnData) ) { // The image column doesn't exist and couldn't be added. return; } String sFolder = temporaryImages.Folder; if (sFolder == null) { // No temporary images were created, so nothing more needs to be // done. return; } // Reduce the key and image column data to visible areas only. Range oVisibleKeyColumnData, oVisibleImageColumnData; if ( !ExcelUtil.TryGetVisibleRange(oKeyColumnData, out oVisibleKeyColumnData) || !ExcelUtil.TryGetVisibleRange(oImageColumnData, out oVisibleImageColumnData) ) { return; } Int32 iAreas = oVisibleKeyColumnData.Areas.Count; if (iAreas != oVisibleImageColumnData.Areas.Count) { return; } // Get the size of each image, in points. SizeF oImageSizePt = GetImageSizePt(temporaryImages.ImageSizePx, workbook); // Get any old images in the image column as a dictionary. This // significantly speeds up the deletion of the old images, because // Excel doesn't have to do a linear search on Shape.Name as each image // is deleted by PopulateAreaWithImages(). Debug.Assert(oTable.Parent is Worksheet); Dictionary<String, Microsoft.Office.Interop.Excel.Shape> oOldImagesInColumn = GetImagesInColumn( (Worksheet)oTable.Parent, imageColumnName ); // Populate each area of the image column with images. workbook.Application.ScreenUpdating = false; try { for (Int32 iArea = 1; iArea <= iAreas; iArea++) { PopulateAreaWithImages(oVisibleKeyColumnData.Areas[iArea], oVisibleImageColumnData.Areas[iArea], imageColumnName, oImageSizePt, oOldImagesInColumn, temporaryImages); } } finally { workbook.Application.ScreenUpdating = true; } // Delete the entire temporary folder. try { Directory.Delete(sFolder, true); } catch (IOException) { // A user reported the following exception thrown from the above // Directory.Delete() call: // // "System.IO.IOException: The directory is not empty.: // // Others have reported this happenning at random times. For // example: // // http://forums.asp.net/p/1114215/1722498.aspx // // I have also seen it happen from the command line outside of // .NET. When it occurs, the directory IS empty but cannot be // accessed in any way. The directory disappears when the machine // is rebooted. // // I can't figure out the cause or the fix. Ignore the problem, // which seems to be benign. } }
//************************************************************************* // Method: CreateAndSaveSubgraphImages() // /// <summary> /// Creates images of a subgraph for one of a graph's vertices and saves /// the images to disk. /// </summary> /// /// <param name="oSubgraph"> /// The subgraph to create images for. /// </param> /// /// <param name="sVertexName"> /// Name of the vertex the subgraph is for. /// </param> /// /// <param name="oCreateSubgraphImagesAsyncArgs"> /// Contains the arguments needed to asynchronously create subgraph images. /// </param> /// /// <param name="oThumbnailImages"> /// Keeps track of the thumbnail images this method creates and stores in a /// temporary folder. /// </param> /// /// <remarks> /// This method creates zero, one, or two images of a subgraph and saves /// them to disk. /// </remarks> //************************************************************************* protected void CreateAndSaveSubgraphImages( IGraph oSubgraph, String sVertexName, CreateSubgraphImagesAsyncArgs oCreateSubgraphImagesAsyncArgs, TemporaryImages oThumbnailImages ) { Debug.Assert(oSubgraph != null); Debug.Assert( !String.IsNullOrEmpty(sVertexName) ); Debug.Assert(oCreateSubgraphImagesAsyncArgs != null); Debug.Assert(oThumbnailImages != null); AssertValid(); if (oCreateSubgraphImagesAsyncArgs.SaveToFolder) { CreateAndSaveSubgraphImageInFolder(oSubgraph, sVertexName, oCreateSubgraphImagesAsyncArgs); } if (oCreateSubgraphImagesAsyncArgs.CreateThumbnails) { CreateAndSaveThumbnailImage(oSubgraph, sVertexName, oCreateSubgraphImagesAsyncArgs, oThumbnailImages); } }
//************************************************************************* // Method: CreateAndSaveThumbnailImage() // /// <summary> /// Creates a thumbnail image of a subgraph for one of a graph's vertices. /// </summary> /// /// <param name="oSubgraph"> /// The subgraph to create an image for. /// </param> /// /// <param name="sVertexName"> /// Name of the vertex the subgraph is for. /// </param> /// /// <param name="oCreateSubgraphImagesAsyncArgs"> /// Contains the arguments needed to asynchronously create subgraph images. /// </param> /// /// <param name="oThumbnailImages"> /// Keeps track of the thumbnail images this method creates and stores in a /// temporary folder. /// </param> //************************************************************************* protected void CreateAndSaveThumbnailImage( IGraph oSubgraph, String sVertexName, CreateSubgraphImagesAsyncArgs oCreateSubgraphImagesAsyncArgs, TemporaryImages oThumbnailImages ) { Debug.Assert(oSubgraph != null); Debug.Assert( !String.IsNullOrEmpty(sVertexName) ); Debug.Assert(oCreateSubgraphImagesAsyncArgs != null); Debug.Assert(oCreateSubgraphImagesAsyncArgs.CreateThumbnails); Debug.Assert(oThumbnailImages != null); AssertValid(); if (oThumbnailImages.Folder == null) { // Create a temporary folder where the thumbnail images will be // stored. String sTemporaryFolder = Path.Combine( Path.GetTempPath(), Path.GetRandomFileName() ); Directory.CreateDirectory(sTemporaryFolder); oThumbnailImages.Folder = sTemporaryFolder; } // Save the graph to a bitmap. Bitmap oBitmap = CreateSubgraphImage(oSubgraph, oCreateSubgraphImagesAsyncArgs, oCreateSubgraphImagesAsyncArgs.ThumbnailSizePx); try { // Save the bitmap in the temporary folder. String sTemporaryFileName = SaveSubgraphImage(oBitmap, oThumbnailImages.Folder, sVertexName, oCreateSubgraphImagesAsyncArgs ); // Add the file name to the dictionary. They key is the vertex // name and the value is the file name, without a path. oThumbnailImages.FileNames[sVertexName] = sTemporaryFileName; } finally { GraphicsUtil.DisposeBitmap(ref oBitmap); } }
//************************************************************************* // Method: CreateSubgraphImagesInternal() // /// <summary> /// Creates an image of a subgraph for each of a graph's vertices and saves /// the images to disk. /// </summary> /// /// <param name="oCreateSubgraphImagesAsyncArgs"> /// Contains the arguments needed to asynchronously create subgraph images. /// </param> /// /// <param name="oBackgroundWorker"> /// A BackgroundWorker object. /// </param> /// /// <param name="oDoWorkEventArgs"> /// A DoWorkEventArgs object. /// </param> //************************************************************************* protected void CreateSubgraphImagesInternal( CreateSubgraphImagesAsyncArgs oCreateSubgraphImagesAsyncArgs, BackgroundWorker oBackgroundWorker, DoWorkEventArgs oDoWorkEventArgs ) { Debug.Assert(oCreateSubgraphImagesAsyncArgs != null); Debug.Assert(oBackgroundWorker != null); Debug.Assert(oDoWorkEventArgs != null); AssertValid(); // Create an object to keep track of the thumbnail images this method // creates and stores in a temporary folder. TemporaryImages oThumbnailImages = new TemporaryImages(); oThumbnailImages.ImageSizePx = oCreateSubgraphImagesAsyncArgs.ThumbnailSizePx; oDoWorkEventArgs.Result = oThumbnailImages; ICollection<IVertex> oVertices; if (oCreateSubgraphImagesAsyncArgs.SelectedVerticesOnly) { oVertices = oCreateSubgraphImagesAsyncArgs.SelectedVertices; } else { oVertices = oCreateSubgraphImagesAsyncArgs.Graph.Vertices; } Int32 iSubgraphsCreated = 0; Boolean bSaveToFolder = oCreateSubgraphImagesAsyncArgs.SaveToFolder; Boolean bCreateThumbnails = oCreateSubgraphImagesAsyncArgs.CreateThumbnails; if (bSaveToFolder || bCreateThumbnails) { foreach (IVertex oVertex in oVertices) { if (oBackgroundWorker.CancellationPending) { if (oThumbnailImages.Folder != null) { // Delete the entire temporary folder. Directory.Delete(oThumbnailImages.Folder, true); oThumbnailImages.Folder = null; } oDoWorkEventArgs.Cancel = true; break; } String sVertexName = oVertex.Name; oBackgroundWorker.ReportProgress(0, String.Format( "Creating subgraph image for \"{0}\"." , sVertexName ) ); // Create a subgraph for the vertex. IGraph oSubgraph = CreateSubgraph(oVertex, oCreateSubgraphImagesAsyncArgs); // Create and save images for the subgraph. CreateAndSaveSubgraphImages(oSubgraph, sVertexName, oCreateSubgraphImagesAsyncArgs, oThumbnailImages); iSubgraphsCreated++; } } oBackgroundWorker.ReportProgress(0, String.Format( "Done. Created {0} subgraph {1}." , iSubgraphsCreated.ToString(ExcelTemplateForm.Int32Format), StringUtil.MakePlural("image", iSubgraphsCreated) ) ); }