I’ve been asked a couple of times now about how to save one of our UML diagrams out to an image file. We don’t have a menu item that does that exact thing in the diagrams themselves. We of course support a select-all, copy, then paste which satisfies most, but a few customers have been asking how they can automate that process for documentation generation or what have you.

Turns out it is actually quite easy to write a simple extension to do exactly this.

Below is the code required to add a menu item that will appear as a context menu in any of the five UML diagrams shipping in VS2010 ( UML class, sequence, activity, component, and use case diagrams ).

The piece of “magic” in the code below is this line:

Diagram dslDiagram = Context.CurrentDiagram.GetObject<Diagram>();

You can ask an IDiagramContext to give you a Microsoft.VisualStudio.Modeling.Diagrams.Diagram object via the GetObject<>() method. For those of you familiar with the DSL programming model, this is indeed a DSL Diagram object, which gives you access to the public ( and documented ) DSL APIs that we have built the UML diagrams on.

We’ve tried to simplify the APIs needed to manipulate the UML object models, which is why we didn’t make the DSL underlying implementation more obvious.

Regardless, here’s the code needed to get a menu item called “Save To Image…” on your UML diagrams. Most of the code is manipulating the SaveFileDialog object. :)

This will work in the current RC build for VS2010.

Enjoy!

Cameron

using System;
using System.Linq;
using System.ComponentModel.Composition;
using Microsoft.VisualStudio.ArchitectureTools.Extensibility.Presentation;
using Microsoft.VisualStudio.ArchitectureTools.Extensibility.Uml;
using Microsoft.VisualStudio.Modeling.ExtensionEnablement;
using Microsoft.VisualStudio.Uml.Classes;
using Microsoft.VisualStudio.Modeling.Diagrams;
using System.Drawing;
using System.Drawing.Imaging;
using System.Windows.Forms;

namespace SaveToImage
{
    // Custom context menu command extension
    // See http://msdn.microsoft.com/en-us/library/ee329481(VS.100).aspx
    [Export( typeof( ICommandExtension ) )]
    [ClassDesignerExtension]
    [UseCaseDesignerExtension]
    [SequenceDesignerExtension]
    [ComponentDesignerExtension]
    [ActivityDesignerExtension]
    class CommandExtension : ICommandExtension
    {
        [Import]
        IDiagramContext Context { get; set; }


        public void Execute( IMenuCommand command )
        {
            Diagram dslDiagram = Context.CurrentDiagram.GetObject<Diagram>();

            if( dslDiagram != null )
            {
                SaveFileDialog dialog = new SaveFileDialog();
                dialog.AddExtension = true;
                dialog.DefaultExt = "image.bmp";
                dialog.Filter = "Bitmap ( *.bmp )|*.bmp|JPEG File ( *.jpg )|*.jpg|Enhanced Metafile (*.emf )|*.emf|Portable Network Graphic ( *.png )|*.png";
                dialog.FilterIndex = 1;
                dialog.Title = "Save Diagram to Image";

                if( dialog.ShowDialog() == DialogResult.OK && !string.IsNullOrEmpty( dialog.FileName))
                {
                    Bitmap bitmap = dslDiagram.CreateBitmap( dslDiagram.NestedChildShapes, Diagram.CreateBitmapPreference.FavorClarityOverSmallSize );
                    bitmap.Save( dialog.FileName, GetImageType( dialog.FilterIndex ));
                }
            }
        }

        public void QueryStatus( IMenuCommand command )
        {
            if( Context.CurrentDiagram != null && Context.CurrentDiagram.ChildShapes.Count() > 0 )
            {
                command.Enabled = true;
            }
            else
            {
                command.Enabled = false;
            }
        }

        public string Text
        {
            get { return "Save To Image…"; }
        }

        private ImageFormat GetImageType( int filterIndex )
        {
            ImageFormat result = ImageFormat.Bmp;

            switch( filterIndex )
            {
                case 2:
                    result = ImageFormat.Jpeg;
                    break;
                case 3:
                    result = ImageFormat.Emf;
                    break;
                case 4:
                    result = ImageFormat.Png;
                    break;
            }
            return result;
        }
    }
}