Welcome to MSDN Blogs Sign in | Join | Help

VisioAutoExt v4.0.0 and Visio Helper Add-In Released

It’s done. I’ve made some substantial changes since the last released version.

Download it here: http://www.codeplex.com/visioautoext

The Visio Helper has been updated to use this library: http://www.codeplex.com/visioautoext/Release/ProjectReleases.aspx?ReleaseId=15831

 

Key improvements

  • DOM-based rendering – for some time the library has included a way to create an in-memory DOM that represents a drawing and then render the DOM into visio. The advantages of the DOM-based technique are that it dramatically simplifies the usage of the Visio API (without concealing its naure) and does so while maintaining high-speed (the DOM is rendered with SetFormulas and DropMany automatically.)
  • Color support – setting colors much easier (CMYK, HSL, HSV, RGB)
  • Support getting Formulas and Results in batch (see QueryFormulas and QueryResults)
  • Rationalized the API and removed unused code
  • Better use of Linq and C# 3.0 features
  • the DemoApp project will allow you to see the output produced by the all the samples

 

Over time I’ll blog about the improvements, what I learned, and show you how I am using the library for my own projects.

Posted by saveenr | 1 Comments

VisioAutoExt: simplifying DropMany to quickly draw a grid

In this example I’ll show how to draw a grid of 100 shapes really fast.

Visio’s Page object comes with a DropMany method that makes putting shapes on the page very fast.

In the SDK code provided, DropMany isn’t too hard in VB but in C# it will look a bit uglier. Also, it only drops the shapes to a position, it doesn’t change the width or height so that a mster fits a specific region. Fortunately, the VisioAutomation.Extensions namespace provides an overloaded DropMany that simplifies it.

 

First, let’s create the basic things we need

var app = new IVisio.ApplicationClass();
var doc = app.Documents.Add(new Size(4, 4));
var page = doc.Pages[1];

Now, let’s load a stencil and pick the master we wish to drop (in this case a rectangle). The OpenStencil method is another extension method provided in VisioAutomation.Extensions and does exactly what the name says.

var stencil = page.Application.Documents.OpenStencil("basic_u.vss");
var master = stencil.Masters["Rectangle"];

At some point, we need to provide a rectangle for each dropped shape to fit into, the Isotope.Drawing.Layout.Grid class is going to make this simple.

var page_rect = page.GetRect();
int num_cols = 10;
int num_rows = 10;
var layout = new Isotope.Drawing.Layout.Grid(page_rect, num_cols, num_rows);

And now we generate a list of all the rectangles in the grid.

var rects = layout.EnumCells(num_cols, num_rows)
    .Select(grid_cell => grid_cell.Rect)
    .ToList();

We need to call DropMany with an array of masters, so we create one with this line of code. The array just contains a single master. So we have 1 master and 100 rects. That means that 1 master will be used 100 times, once for each rect.

var masters = new [] { master };

And finally we call DropMany

var shape_ids = page.DropMany(masters, rects);

 

And we are done. This code will quickly produce a new document with a single page (4”x4”) that is filled with 100 squares.

 

Notes

  • There’s another DropMany extension method that accepts points instead of rects

 

The full sample code

 

using System;
using System.Collections.Generic;
using System.Linq;
using Isotope.Drawing;
using IVisio = Microsoft.Office.Interop.Visio;
using VisioAutomation;
using VisioAutomation.Extensions;
using Isotope.Linq.Extensions;

namespace CmdLineTest
{
    internal class Program
    {
        [STAThread]
        private static void Main(string[] args)
        {

            var app = new IVisio.ApplicationClass();
            var doc = app.Documents.Add(new Size(4, 4));
            var page = doc.Pages[1];

            var stencil = page.Application.Documents.OpenStencil("basic_u.vss");
            var master = stencil.Masters["Rectangle"];
            var page_rect = page.GetRect();
            int num_cols = 10;
            int num_rows = 10;
            var layout = new Isotope.Drawing.Layout.Grid(page_rect, num_cols, num_rows);

            var rects = layout.EnumCells(num_cols, num_rows)
                .Select(grid_cell => grid_cell.Rect)
                .ToList();

            var masters = new[] { master };
            var shape_ids = page.DropMany(masters, rects);

            /*

        }
    }
}

Posted by saveenr | 0 Comments

VisioAutoExt: Preparing for an new release and new some sample code

This week I'll be releasing a major update to the VisioAutoExt project (v4.0). In preparation for the release I will be documenting the usage of the library via a series of blog posts. This is the first one.

First, what's in the library?

This diagram was automatically drawn (and required no manual modifications) by a new tool in the VisioAutoExt solution called MakeAssemblyPoster.

image

 

A Walkthrough

I'll describe a very simple task: setting the foreground fill of a shape to Red. We'll start with the "normal" way of doing it and then show how the library can be used

 

The original code

using System;
using System.Collections.Generic;
using System.Linq;
using Isotope.Drawing;
using IVisio = Microsoft.Office.Interop.Visio;
using VisioAutomation.Extensions;
using VisioAutomation.Extensions.Helpers;
using VA=VisioAutomation;

namespace CmdLineTest
{
    internal class Program
    {
        [STAThread]
        private static void Main(string[] args)
        {
            var app = new IVisio.ApplicationClass();
            var doc = app.Documents.Add(new Size(4, 4));
            var page = doc.Pages[1];

            var rect = new Rect(1, 1, 3, 3);
            var shape0 = page.DrawRectangle(rect);

            shape0.get_CellsSRC(
                
(short) IVisio.VisSectionIndices.visSectionObject,
                 (short) IVisio.VisRowIndices.visRowFill,
                
(short) IVisio.VisCellIndices.visFillForegnd )
                .FormulaU = "rgb(255,0,0)";

        }
    }
}

 

now using the CellsSRC extension method in VisioAutomation.Extensions

            shape0.CellsSRC( VA.Cells.FillForegnd )
               
.FormulaU = "rgb(255,0,0)";

Using the the SetFormulaU() extension method makes this a little simpler

            shape0.CellsSRC( VA.Cells.FillForegnd )
                .SetFormulaU(  "rgb(255,0,0)"  );

and SetFormulaU() lets us use other types like this ...

shape0.CellsSRC(VA.Cells.FillForegnd)
    .SetFormulaU(  new Isotope.Drawing.ColorRGB24Bit(255,0,0)  );

And there are a predefined set of colors found in VisioAutomation.Colors.SystemColors

            shape0.CellsSRC( VA.Cells.FillForegnd )
                .SetFormulaU(  VA.Colors.SystemColors.Red  );

 

or use System.Drawing.Color

            shape0.CellsSRC( VA.Cells.FillForegnd )
                .SetFormulaU(  System.Drawing.Color.Red  );

or CMYK

            shape0.CellsSRC(VA.Cells.FillForegnd)
                .SetFormulaU(  new Isotope.Drawing.ColorCMYK(0, 0.65, 0.85, 0)  );

 

or HSV

            shape0.CellsSRC(VA.Cells.FillForegnd)
                .SetFormulaU(  new Isotope.Drawing.ColorHSV(1.0,1.0,1.0)  )

or HSL

shape0.CellsSRC(VA.Cells.FillForegnd)
    .SetFormulaU(  new Isotope.Drawing.ColorHSL(1.0,1.0,0.5)  );

or just "cheat" and use the SetFillForegroundColor() method  in the VisioAutomation.Extension.Helpers namespace

shape0.SetFillForegroundColor(  VA.Colors.SystemColors.Red  );

 

NOTE: By now you may have noticed that SetFormulaU() accepts many color types. You might believe that SetFormulaU() contains multiple overloads, one for each color type. In fact it does not. Instead it uses the C# implicit operator with VisioAutiomation.Color class to support multiple color types with only one method.

A Simple LINQ Example with System.Reflection and System.Drawing.Color

Recently I needed to get see all the RGB values for the System.Drawing.Color values (for example System.Drawing.Color.Red, System.Drawing.Color.Aqua, etc.).

It's a very simple task that shows some of the expressive simplicity of LINQ.

Here's the sample using the LINQ method syntax:

using System;
using System.Linq;

namespace CmdLineTest
{
    internal class Program
    {
        [STAThread]
        private static void Main(string[] args)
        {
            var color_type = typeof(System.Drawing.Color);

            var color_properties = color_type.GetProperties()
                .Where(m => m.PropertyType == color_type );

            foreach (var color_property in color_properties)
            {
                var cur_color = (System.Drawing.Color)color_property.GetValue(null, null);
                uint rgb_int = (uint)cur_color.ToArgb() & (0x00ffffff);
                Console.WriteLine(" {0} = ({1},{2},{3}) = 0x{4:X00000000}",
                    color_property.Name,
                    cur_color.R, cur_color.G, cur_color.B,
                    rgb_int );
            }
        }
    }
}

Here's the sample using the LINQ query syntax:

using System;
using System.Linq;

namespace CmdLineTest
{
    internal class Program
    {
        [STAThread]
        private static void Main(string[] args)
        {
            var color_type = typeof(System.Drawing.Color);

            var color_properties = from prop in color_type.GetProperties()
                                   where prop.PropertyType == color_type
                                   select prop;

            foreach (var color_property in color_properties)
            {
                var cur_color = (System.Drawing.Color)color_property.GetValue(null, null);
                uint rgb_int = (uint)cur_color.ToArgb() & (0x00ffffff);
                Console.WriteLine(" {0} = ({1},{2},{3}) = 0x{4:X00000000}",
                    color_property.Name,
                    cur_color.R, cur_color.G, cur_color.B,
                    rgb_int );
            }
        }
    }
}

The output will look like this

Transparent = (255,255,255) = 0xFFFFFF
AliceBlue = (240,248,255) = 0xF0F8FF
AntiqueWhite = (250,235,215) = 0xFAEBD7
Aqua = (0,255,255) = 0xFFFF
Aquamarine = (127,255,212) = 0x7FFFD4
Azure = (240,255,255) = 0xF0FFFF
Beige = (245,245,220) = 0xF5F5DC
Bisque = (255,228,196) = 0xFFE4C4
Black = (0,0,0) = 0x0
BlanchedAlmond = (255,235,205) = 0xFFEBCD
Blue = (0,0,255) = 0xFF
BlueViolet = (138,43,226) = 0x8A2BE2
Brown = (165,42,42) = 0xA52A2A
BurlyWood = (222,184,135) = 0xDEB887
CadetBlue = (95,158,160) = 0x5F9EA0
Chartreuse = (127,255,0) = 0x7FFF00

For the record: my favorite is System.Drawing.Color.Tomato

Posted by saveenr | 2 Comments

Attachment(s): colors.txt

Visio Helper Add-In Demo Video

Below is a SilverLight Streaming video that demonstrates the latest version of my Visio Helper Add-in for Visio 2007.

 

Posted by saveenr | 0 Comments

Visio Helper Add-In Update – New features and now with an Installer

The Visio Helper Add-In is now available in binary form with a setup program. This means you no longer require Visual Studio 2008 to use the tool.

 

Instructions:

NOTE: the installer will install any prerequisites if they are missing (.NET Framework 3.5, VSTO 3.0)

 

Menu Items

image image

imageimage

image

 

Tools

image 

image

image

 

image

 

image

image

 

image

 

image

 

 

image

Posted by saveenr | 3 Comments

Practical Tools for High-Performance Meetings

Sometimes setting up a clear agenda, goals, and action items is not enough. Sometimes you need to take your meeting to the next level (11).

Below are tools to cover three common meeting scenarios

 

(1) recover from a tangent

rathole.mp3 (Merlin Mann from MacBreak Weekly Episode 30)

(2) emphasize your point

http://www.instantrimshot.com/

(3) negotiate with a partner team

http://sadtrombone.com/

Posted by saveenr | 0 Comments

Attachment(s): Rat_Hole.mp3

Whence my interest in Visio?

Looking back over the past year, I’ve created many posts on Visio. I’ve never made it clear why I am so passionate about a product I don’t work on. In this post I’m going to elaborate on my interest.

My background

First, I’ve had an interest in computer graphics for a long time. For example, I owned a KoalaPad over twenty years ago. In the 1980’s I was an avid user of NEOChrome, Degas, Deluxe Paint, Photon Paint. [1] Low resolutions, small color palettes, slow hardware , inconsistent UI - it was a primitive time. [2] But despite those limitations it was a new avenue to be creative – and be creative what I mean is satisfying that fundamental human impulse to make something out of nothing. [3]

 

Deluxe Paint 5.0 (screenshot from Wikipedia)

Image:DPVAboutBoxWithImage.png

 

Deluxe Paint 5.0 (screenshot from Wikipedia)

Image:DPVScreenFormat.png

 

PhotonPaint (screenshot from Wikipedia)

Image:HAM6example.png

Joining Microsoft

I almost pursued a career as a physician and despite (still) having a great curiosity about biology and medicine, I knew computers and technology and the world it opened up for people was where I truly wanted to be. In 1996, I interviewed at Microsoft hoping to work on something related to multimedia, but the team that I interviewed with was Microsoft Exchange and they were such fascinating set of people working on amazing things that I accepted the offer and began working on Microsoft Exchange on the version the world knows as Microsoft Exchange 5.0. I didn’t work on graphics, I worked on the component of Exchange that translated messages from internet formats used by SMTP, IMAP, POP, NNTP into the internal format used by Exchange Server.

Trivia for email buffs – I designed the infamous and sarcastic “TNEF is cool” T-shirt you might have seen some Exchange team members wear.  Everyone rightly hated TNEF.

 

Back to the Pixels

The allure of those little squares of color was unbearable, I left exchange in 1998 to join the PhotoDraw team. They had just released Microsoft PhotoDraw 2000 and wanted to get a major update as soon as possible. This PhotoDraw 2000 V2 released in 1999 I believe almost exactly a year after “V1”.

Despite my long interest as a user who worked with graphics, this was the first time I started learning about the technology. It was fascinating and let me assure you, despite its flaws PhotoDraw 2000 V2 was and continues to be one of the most technically and conceptually advanced graphics apps that has ever existed [4] [5].

I keep a Virtual Machine around with PhotoDraw installed, just in case.

PhotoDraw 2000 V2 running on Vista SP1 as a Hyper-V Virtual Machine 

image

In PhotoDraw 2000 V2 I worked on Setup, Scanners & Cameras, and a brand-new feature: the Batch Save Wizard. This was my first work in User Experience of any kind.

 

PhotoDraw 2000 V2’s Batch Save Wizard

image image image

A Bunch of Things Happened

PhotoDraw was canceled due to an internal reorganization in Microsoft. The short story is I joined a team focused on real-time collaboration (IM, telephony, desktop sharing) because I saw this as the logical evolution in my thinking about supporting the creative impulse – it allowed me to focus on how people collaborate to create as a group. More reorganizations followed and I joined a division working on security – my thinking here was that malware (viruses,etc.) was making it increasingly difficult to even use computers to create. I was happy to help create a more fundamentally secure environment for customers so that they could once again go back to satisfying their creative impulse. After over 4 years in security, I joined the Dynamics team to work on Business Intelligence and I saw this as a way of taking my background in graphics and UX and visualization and contributing towards a growing business at Microsoft.

Back to Visio and PhotoDraw

I always wanted to be able to take PhotoDraw and implement my own extensions (wizards, tools, etc) to both add features I needed and also to prototype new ideas. However, doing this was not so easy – MFC, C++, COM, Office APIs, heap management – and the ratio of effort to reward was not worth it.

In 2003, I started using Visio – I could probably find the first Visio diagram I ever drew. At first, I created flowcharts or simple network topology diagrams and the more I used it the more productive I became. I started using it for increasingly more conceptual diagrams and my thinking about the application evolved.

I viewed it more as tool that both satisfied the creative impulse, let me organize my thoughts, and allow me to communicate and collaborate more effectively. What I found was that the visual clarity helped drive the conceptual clarity and this, in turn, made me and my team more effective in our jobs.

And then the coding environment got *much* better – Visual Studio, .NET, C#, VST0, Visio’s Automation API – suddenly I found myself able to extend and experiment with this tool and do the things that I always wanted to try with PhotoDraw. So then I am bending the tool to my customized needs and it isn’t even taking that much effort.

Below are some examples of how I have extended Visio 2007.

image image image

Ultimately what I get out of all this is I get to take an amazing application and set of technologies and make them all support my own creative impulse and help me think and communicate and collaborate better.

 

And that is really cool.

 

 

Notes

[1] It may be an artifact of my memory, but I don’t believe I’ve found a bitmap graphics app that is as productive today in 2008 as Deluxe Paint was in the 80’s.

 

[2] At first if you wanted a “gradient fill” you had to manually place each pixel. Yes, that was tedious.

 

[3] Nostalgia – acute homesickness

“nostalgia” <- “nostos” + “algia”

“nostos” – (greek) to return home

“algia” – (greek) pain

 

[4] I can at least a couple of posts about this.

 

[5] And then I’ll tell you about what happened to the application and what we were planning to give you in the third version.

Posted by saveenr | 1 Comments

Simplifying Visio Automation with C# 3.0, Linq, and delegates

I saw a great post by the Visio Guy today ( Save Time & Simplify Your VBA Code With CallByName ) and thought it would be a good opportunity to demonstrate some of the simplification that you can achieve with the new features in C# 3.0 and using the VisioAutoExt library.

The point of his post was that you could use CallByName in VBA to reuse an algorithm – in this case applying a function to each element in a set of shapes. The sample code below does the same thing, but I hope you see how elegant it is in C# 3.0

 

Key points

  • The “using VisioAutomation.Extensions” and “Using System.Linq” statements are critical to making this work.
  • The ProcessShapes methods allows you to apply a method to a Visio Shapes collection or any IEnumerable set of shapes.

To use the ProcessShapes method

ProcessShapes( page.Shapes , SomeMethod );

or you can even do this …

ProcessShapes( page.Shapes , shape => { shape.SetFillForefroundColor( System.Drawing.Red ) } );

The FullSource Code

Below I’ve translated the source code from his original post into the C# 3.0 equivalent.

using System;
using System.Collections.Generic;
using System.Linq;
using IVisio = Microsoft.Office.Interop.Visio;
using VisioAutomation.Extensions;

namespace VisioAutomationSamples
{

    public partial class Demo
    {

        public static void sample4()
        {
            var visapp = new Microsoft.Office.Interop.Visio.ApplicationClass();
            var doc = visapp.Documents.Add(new Isotope.Drawing.Size(10, 10));
            var page = doc.Pages[1];

            draw_stuff(page);

            ProcessShapes(page.Shapes, MakeRed );
            ProcessShapes(page.Shapes, RotateLeft );
            ProcessShapes(page.Shapes, SetText );

        }

        public static void ProcessShapes( IEnumerable<IVisio.Shape> shapes , Action<IVisio.Shape> func)
        {

            foreach (var shape in shapes)
            {
                func(shape);
            }
        }

        public static void ProcessShapes( IVisio.Shapes shapes, Action<IVisio.Shape> func)
        {
            foreach (IVisio.Shape shape in shapes)
            {
                func(shape);
            }
        }

        public static void draw_stuff( IVisio.Page  page)
        {
            // Draw some rectangles
            int num_rows = 3;
            int num_cols = 3;
            var grid = new Isotope.Drawing.LayoutGrid(page.GetRect(), num_rows, num_cols, false);

            var cells = grid.EnumCells(num_rows, num_cols);
            var shapes = cells.Select(c => page.DrawRectangle(c.rect)).ToList();
        }

        public static void MakeRed( IVisio.Shape shape )
        {
            shape.SetFillForegroundColor( System.Drawing.Color.Red );
        }

        public static void RotateLeft( IVisio.Shape shape )
        {
            if ( shape.OneD == VisioAutomation.VisUtil.BoolToShort(false) )
            {
                double angle =
                    shape.CellsSRC(VisioAutomation.VisProps.ShapeRotation)
                    .get_Result(IVisio.VisUnitCodes.visDegrees);

                double new_angle = angle + (45.0*System.Math.PI/180.0);

                shape.CellsSRC(VisioAutomation.VisProps.ShapeRotation).ResultIU = new_angle;
            }

        }

        public static void SetText(IVisio.Shape shape)
        {
            shape.Text = "Hello World";
        }

    }

}

Posted by saveenr | 2 Comments

Visio Helper Add-in: Updates – Fill Designer improvements, Copy/Paste Size, Snap Size, Snap Position, Duplicate Page, etc.

Quick summary

  • New updates available to the  VisioAutoExt library
  • Gradient Helper renamed to Fill Designer
  • Fill Designer is a now a non-modal anchor window
  • Can copy the size of one shape and paste that size into another shape
  • Can snap shape sized (hard-coded to 0.25 inches)
  • Can snap the position (lower left corner) of a shape to the nearest 0.25 coordinate
  • Close all documents without saving
  • Duplicate a page
  • Still no setup available – use VS2008 to build and run the add-in

Suggestions?

  • Have something that you often do in Visio? let me know and I’ll see if it makes sense to incorporate as part of this add-in

Demo Video

Posted by saveenr | 3 Comments

Visio: “Visio Helper” an Add-In for Visio 2007 to optimize common tasks

Quick summary

  • The VisioAutoExt library now contains an Add-In to Visio2007 called “Visio Helper”
  • It’s an experimental add-in to help me optimize Visio for my tasks – creating new documents and drawing nice gradient fills
  • No setup.exe available yet, I run it by loading the project in Visual Studio 2008 and staring a debugging session
  • Beware: this is very early prototype code

Suggestions?

  • Have something that you often do in Visio? let me know and I’ll see if it makes sense to incorporate as part of this add-in

Demo

  • It takes a while to explain, and it’s easier for me to demonstrate, so I’ve uploaded a video to MSN Soapbox Videos

Posted by saveenr | 1 Comments

Visio: Programmatically creating an org chart from a simple text file

As a believer in the power of simple text formats, I wrote a tool to help make it easy to quickly draw org charts in visio by starting in notepad. It’s useful for more than “org charts” youcan use it to quickly render any simple hierarchical data.

 

Quick summary

  • The VisioAutoExt library now contains a command-line tool called MakeOrgChart
  • MakeFlowChart accepts simple indented text file draws an org chart based on the indentation level

Command-Line Usage

C:\>MakeOrgChart.exe d:\input1.txt

Example 1 – the input text file

 

Charlie
    Tim
        Chris
        Mort
    Julia
        Alice
        Akuma

 

Example 1 – the output drawing

image

 

Example 2 – the input text file

This example illustrates

  • a much larger orgchart
    • these org charts will tend to be very horizontal. you’ll have to use Visio’s org chart tools to re-layout sections to meet your needs
    • The larger these get, the longer it takes visio to draw them using its layout algorithm.
  • multiple roots nodes in the org chart
    • the drawing will compensate by adding a placeholder “ROOT” node, which you can manually delete
  • using the vertical bar character to add linebreaks in the the text

 

Charlie | President and CEO
    Tim | IT Manager
        Chris | Operations Engineer
        Simon | System Implementer
        Isaac | ISV Biz App Developer
        Mort | IT Systems Developer       
        Sean | Power User 
        Ivar | Inexp. VAR Sys Imp 

    Julia | Marketing Executive
        Kevin | Sales Manager
        Nicole | Marketing Staffer
        Benjamin | Marketing Manager
        David | Dedicated Sales Rep
        Michael | Account Manager 
        Susan | Order Processor 

    Marie | Cust. Serv Ops Mngr 
        Lisa | Cust. Service Rep 
        Daniel | Dispatcher 
        Rebecca | Receptionist/Admin 
        Terrence | Outbound Technician 
    Vince  | Operations Manager 
        Karl | Materials Manager 
        Ellen | Warehouse Manager 
        Inga | Purchasing Manager 
        Sammy | Shipping & Receiving 
        Alicia | Purchasing Agent 
        John | Warehouse Worker 
        Ted | Transportation Coord. 
        Tony | Production Manager 
        Lars | Shop Supervisor 
        Oscar | Process Engineer 
        Shannon | Machine Operator 
        Eduardo | Production Planner 
        Heather | Human Resources 
        Ricardo | Quality Assurance 
        Emil | Product Designer 
        June | Dir. of Prof. Services 
        Prakash | Project Manager 
        Reina | Resource Manager 
        Tricia | Project Team Member 
    Sara | CFO 
        Phyllis | Accounting Manager 
        Ken | Controller 
        Cassie | Accountant 
        Penny | Payroll 
        Arnie | Accounts Receivable 
        April | Accounts Payable 
        Annie | Bookkeeper 
        Connie | Credit & Collections Manager 
    Claire | HR Director / Manager 
        Brooke | HR Assistant 
        Grace | Training/Development Manager/ Specialist
        Jacob| Staffing Recruitment Manager/Specialist
        Jodi | Copensation and benifits Manger 
        Luke | HR Generalist

Stan |Small Business Owner 
    Debra | Office Manager 
    Lacy | Store Operations Mngr 
    Jeremy | Cashier 

 

 

Example 2  – the output drawing

Drawing4

Posted by saveenr | 0 Comments

Visio: Using MSAGL to Create Visio Flowcharts Programmatically

Inspired by Chris Roth’s post on creating flowcharts programmatically and my original post on MSAGL with Visio, I’ve uploaded a new version of the VisioAutoExt library that has lets you automatically create flowcharts from a simple XML file.

Quick summary

  • The VisioAutoExt library now contains a command-line tool called MakeFlowChart
  • MakeFlowChart accepts simple XML syntax and can layout a flowchart using MSAGL or Visio to position the flowchart shapes.
  • You are not restricted to flowcharts! It will draw any diagram. Though not all shapes work well with the Dynamic Connector used by the tool

Command-Line Usage

C:\>MakeFlowChart.exe d:\input1.xml

Example 1 – the input XML

<autolayoutdrawing>
  <renderoptions
    usedynamicconnectors="true"
    connectorlineweight="3"
    connectorarrowsize="5"
    resizetofitcontents="true"
    />
  <shapes>
    <shape id="n1" label="FOO1" stencil="server_u.vss" master="Server" />
    <shape id="n2" label="FOO2" stencil="server_u.vss" master="Email Server" />
    <shape id="n3" label="FOO3" stencil="server_u.vss" master="Proxy Server" />
    <shape id="n4" label="FOO4" stencil="server_u.vss" master="Web Server" />
    <shape id="n5" label="FOO4" stencil="server_u.vss" master="Application Server" />
  </shapes>

  <connectors>
    <connector id="c1"  from="n1" to="n2" label="LABEL1" />
    <connector id="c2" from="n2" to="n3" label="LABEL2" />
    <connector id="c3" from="n3" to="n4" label="LABEL1"  />
    <connector id="c4" from="n4" to="n5" label=""/>
    <connector id="c5" from="n4" to="n1" label=""/>
    <connector id="c6" from="n4" to="n3" label=""/>
  </connectors>

</autolayoutdrawing>

 

Example 1 – the output drawing

 

image

Layout – the initial placement of nodes

An initial layout of nodes is always done using MSAGL.

 

Controlling whether to use Dynamic Connectors or Bezier curves

For a proper Visio diagram we should use Dynamic Connectors, but MSAGL draws the edges with Bezier curves.

The usedynamicconnectors attribute controls whether to use dynamic connectors or just draw the bezier curves that MSAGL defines for the drawing.

 

For example, if usedynamicconnectors is set to “false” in Example 1 , the output would look like the image below. The bezier curves look nice, but are cannot respond to editing the diagram, unlike Dynamic Connectors.

image

 

Not satisfied with the MSAGL placement of nodes?

If you specified the use of dynamic connectors, you can always force the output to relayout using Visio’s algorithm via the Shape > Re-layout Shapes menu item.

image

Which will product this result for example 1

image

The node spacing is a bit tight by default. This can again be controlled via Shape > Configure Layout

image

 

image

And picking a larger number for Spacing.

 

Using different stencils and masters

Just specify the stencil and master name in the stencil and master attributes

 

<autolayoutdrawing>
  <renderoptions
    usedynamicconnectors="true"
    connectorlineweight="3"
    connectorarrowsize="5"
    resizetofitcontents="true"
    />
  <shapes>
    <shape id="n1" label="FOO1" stencil="basflo_u.vss" master="Decision" />
    <shape id="n2" label="FOO2" stencil="basflo_u.vss" master="Process" />
    <shape id="n3" label="FOO3" stencil="basflo_u.vss" master="Data" />
    <shape id="n4" label="FOO4" stencil="basflo_u.vss" master="Process" />
    <shape id="n5" label="FOO4" stencil="basflo_u.vss" master="Data" />
  </shapes>

  <connectors>
    <connector id="c1"  from="n1" to="n2" label="LABEL1" />
    <connector id="c2" from="n2" to="n3" label="LABEL2" />
    <connector id="c3" from="n3" to="n4" label="LABEL1" />
    <connector id="c4" from="n4" to="n5" label=""/>
    <connector id="c5" from="n4" to="n1" label=""/>
    <connector id="c6" from="n4" to="n3" label=""/>
  </connectors>

</autolayoutdrawing>

image

Posted by saveenr | 1 Comments

Speeding up Visio automation by batching via SetFormulas(), GetFormulas() and VisDOM

If you've read this post by Bill Morein and this one by Mai-lan, you know that using page.SetFormulas() and page.GetFormulas() can really speed things up when drawing via Automation.

In this post I'm going to show a range of techniques to use SetFormulas() and GetFormulas() to achieve this performance win. The code here is on the latest release (3.0.2) of AutoVisioExt on CodePlex.

First, let's use the VisioAutoExt library and some basic code that draws a 15x15 grid of squares and sets them to have a circular fill gradient with different starting and ending transparencies (something that can't be done in the Vision 2007) UI.

The output should look like this:

image

 

The first attempt without any batching

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using IVisio = Microsoft.Office.Interop.Visio;
using VisioAutomation;

namespace VisioAutomationSamples
{

    public partial class Demo
    {

        public static void sample3_a()
        {
            IVisio.Application visapp = new Microsoft.Office.Interop.Visio.ApplicationClass();
            IVisio.Document doc = visapp.Documents.Add(new Isotope.Drawing.Size(10, 10));

            IVisio.Page page = doc.Pages[1];

            int num_rows = 15;
            int num_cols = 15;
            Isotope.Drawing.LayoutGrid grid = new Isotope.Drawing.LayoutGrid( page.GetRect(), num_rows,num_cols,false);

            var cells = grid.EnumCells(num_rows, num_cols);
            var shapes = cells.Select(c => page.DrawRectangle(c.rect)).ToList();

            format_shapes_no_batching(page, shapes);
        }

        public static void format_shapes_no_batching( IVisio.Page page, IEnumerable<IVisio.Shape> shapes)
        {
            // no batching
            foreach (IVisio.Shape shape in shapes)
            {
                shape.SetFillPattern(FillPatternType.RadialCenter);
                shape.SetFillForegroundColor(System.Drawing.Color.Red);
                shape.SetFillForegroundTransparency(0);
                shape.SetFillBackgroundColor(System.Drawing.Color.Black);
                shape.SetFillBackgroundTransparency(50);
            }
        }

    }

}

 

This example uses the VisioAutoExt library so the code is in general a bit simpler than "pure" Visio automation code. If you run the code, you'll notice the delay as each shape is drawn and then an even greater delay as the fill properties are set on each shape.

 

 

The second attempt using SetFormulas() and GetFormulas()

Now let's take the performance advice and use SetFormulas() and GetFormulas(). We'll replace format_shapes_no_batching() with this function format_shapes_batching_1()

 

public static void format_shapes_batching_1(IVisio.Page page, List<IVisio.Shape> shapes)
{

    List<short> shapesheet_info = new List<short>();

    foreach (IVisio.Shape shape in shapes)
    {
        shapesheet_info.Add((short)shape.ID);
        shapesheet_info.Add((short)IVisio.VisSectionIndices.visSectionObject);
        shapesheet_info.Add((short)IVisio.VisRowIndices.visRowFill);
        shapesheet_info.Add((short)IVisio.VisCellIndices.visFillPattern);
        shapesheet_info.Add((short)shape.ID);
        shapesheet_info.Add((short)IVisio.VisSectionIndices.visSectionObject);
        shapesheet_info.Add((short)IVisio.VisRowIndices.visRowFill);
        shapesheet_info.Add((short)IVisio.VisCellIndices.visFillForegnd);
        shapesheet_info.Add((short)shape.ID);
        shapesheet_info.Add((short)IVisio.VisSectionIndices.visSectionObject);
        shapesheet_info.Add((short)IVisio.VisRowIndices.visRowFill);
        shapesheet_info.Add((short)IVisio.VisCellIndices.visFillForegndTrans);
        shapesheet_info.Add((short)shape.ID);
        shapesheet_info.Add((short)IVisio.VisSectionIndices.visSectionObject);
        shapesheet_info.Add((short)IVisio.VisRowIndices.visRowFill);
        shapesheet_info.Add((short)IVisio.VisCellIndices.visFillBkgnd);
        shapesheet_info.Add((short)shape.ID);
        shapesheet_info.Add((short)IVisio.VisSectionIndices.visSectionObject);
        shapesheet_info.Add((short)IVisio.VisRowIndices.visRowFill);
        shapesheet_info.Add((short)IVisio.VisCellIndices.visFillBkgndTrans);
    }

    System.Array shapesheet_info_array = (System.Array)shapesheet_info.ToArray();

    System.Array formula_array;
    page.GetFormulas(ref shapesheet_info_array, out formula_array);

    int i=0;
    foreach (var shape in shapes)
    {
        formula_array.SetValue("40", i++);
        formula_array.SetValue("rgb(255,0,0)", i++);
        formula_array.SetValue( "0%", i++);
        formula_array.SetValue("rgb(0,0,0)", i++);
        formula_array.SetValue("50%", i++);
    }
    short flags = 0;
    page.SetFormulas(ref shapesheet_info_array, ref formula_array, flags);

}

 

How wonderfully painful. At least it's really fast compared to the first example.

 

 

The third example: Using VisBatchFormulaApplier

We can get much simpler code and all the performance benefits if we use the VisBatchFormulaApplier class found in the VisioAutoExt library.

 

public static void format_shapes_batching_2(IVisio.Page page, IEnumerable<IVisio.Shape> shapes)
{
    VisioAutomation.VisBatchFormulaApplier batch = new VisBatchFormulaApplier();
    foreach (IVisio.Shape shape in shapes)
    {
        batch.SetFormula( shape, VisProps.FillPattern, (int) FillPatternType.RadialCenter );
        batch.SetFormula( shape, VisProps.FillForegroundColor , System.Drawing.Color.Red );
        batch.SetFormula( shape, VisProps.FillForegroundTransparency, 0, VisUnit.Percent);
        batch.SetFormula( shape, VisProps.FillBackgroundColor, System.Drawing.Color.Black);
        batch.SetFormula( shape, VisProps.FillBackgroundTransparency, 50, VisUnit.Percent);

    }
    batch.ApplyFormulas(page);
}

 

It isn't quite as readable as the first example, but it's much better then the second example, but it is very fast.

VisBatchFormulaApplier will generally make things much simpler.

Some issues:

  • it will only with shapes on the same page
  • you have to write your automation code to create all the objects first and then work use the applier.
  • you have to remember that some properties require you to specify the unit to work (for example VisUnit.Percent for transparencies)

 

 

The fourth example: using VisDOM

And now, we use the new experimental VisDOM support. VisDOM is essentially a small set of classes that will automatically draw shapes and apply them in batch. As an extra benefit, things are much more readable and strongly typed than any of the examples above. Here's the full example:

 

public static void sample3_b()
{

    VisDOM.Document doc = new VisDOM.Document();
    VisDOM.Page page1 = new VisDOM.Page(10, 10);
    doc.Pages.Add(page1);

    int num_rows = 15;
    int num_cols = 15;
    Isotope.Drawing.LayoutGrid grid = new Isotope.Drawing.LayoutGrid(page1.Rect, num_rows, num_cols, false);

    var cells = grid.EnumCells(num_rows, num_cols);
    var shapes = cells.Select(c => page1.DrawRectangle(c.rect)).ToList();

    foreach (VisDOM.Shape shape in shapes)
    {
        shape.FillPattern.Value = FillPatternType.RadialCenter;
        shape.FillForegroundColor.Value = System.Drawing.Color.Red;
        shape.FillBackgroundColor.Value = System.Drawing.Color.Black;
        shape.FillForegroundTransparency.Value = 0;
        shape.FillBackgroundTransparency.Value = 50;

    }

    IVisio.Application visapp = new Microsoft.Office.Interop.Visio.ApplicationClass();
    VisDOM.VisDOMRenderer.RenderToVisio(doc, visapp,false,false);

}

 

VisDOM constructs a document in memory and then the VisDOMRenderer.RenderToVisio() method does all the Visio magic for batching.

Notice how the properties like FillForegroundTransparency are strongly typed, will work with Intellisense, and don't require knowledge of the VisUnit codes.

Posted by saveenr | 0 Comments

My Latest enhancements to the VisioAutoExt library

I've published a big update to the VisioAutoExt library on Codeplex. The changes are significant enough with enough breaking changes from the old code that I'm calling this version 3.0.

 

 

Three things I'll talk about in this post:

  • What changed
  • Why it's interesting to use the library
  • How to use the library

 

What changed

  • UPDATES: Lots of renaming and refactoring
  • NEW: Added a DemoVisioAutomation winform application. It makes it easer to see what can be done with the library
  • NEW: Enhanced Batching support via BatchFormulaApplier
  • UPDATES: Lots of the test cases now use the Batching support and so drawing takes place much faster.
  • NEW: An experimental library called VisDOM added to support even simpler and faster drawing in batch and perhaps one day emit Visio XML directly without launching the app
  • UPDATE: Kuler demo updated to latest XML schema, added test cases
  • NEW: VisioHelper a winform app that helps in drawing nice gradients in visio
  • NEW: VisProps, a class that contains predefined section,row,and cell tuples. Makes using CellsSRC much easier

 

Why use it?

This is best illustrated by walking through a simple project (create a new doc and draw a rectangle programmatically).

 

The desired result

The output we want is simple:

image

Step 1: Starting point: let's do this without the VisioAutoExt library

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using IVisio = Microsoft.Office.Interop.Visio;
using VisioAutomation;

namespace VisioAutomationSamples
{

    public partial class Demo
    {

        public static void sample1_a()
        {
            IVisio.Application visapp = new Microsoft.Office.Interop.Visio.ApplicationClass();
            IVisio.Document doc = visapp.Documents.Add("");
            IVisio.Page page = doc.Pages[1];

            page.PageSheet.get_CellsSRC(
                (short)IVisio.VisSectionIndices.visSectionObject,
                (short)IVisio.VisRowIndices.visRowPage,
                (short)IVisio.VisCellIndices.visPageWidth).Formula = "5";
            page.PageSheet.get_CellsSRC(
                (short)IVisio.VisSectionIndices.visSectionObject,
                (short)IVisio.VisRowIndices.visRowPage,
                (short)IVisio.VisCellIndices.visPageHeight).Formula = "5";

            IVisio.Shape shape1 = page.DrawRectangle(1, 1, 2, 2);
        }

    }

}

 

The first improvement: No longer need to use the awkward "get_CellsSRC", now it's just CellsSRC

This makes it look much more like VB code that Visio emits when it records Macros. So that's an easy, cheap win.

public static void sample1_b()
{
    IVisio.Application visapp = new Microsoft.Office.Interop.Visio.ApplicationClass();
    IVisio.Document doc = visapp.Documents.Add("");
    IVisio.Page page = doc.Pages[1];

    page.PageSheet.CellsSRC(
        (short)IVisio.VisSectionIndices.visSectionObject,
        (short)IVisio.VisRowIndices.visRowPage,
        (short)IVisio.VisCellIndices.visPageWidth).Formula = "5";
    page.PageSheet.get_CellsSRC(
        (short)IVisio.VisSectionIndices.visSectionObject,
        (short)IVisio.VisRowIndices.visRowPage,
        (short)IVisio.VisCellIndices.visPageHeight).Formula = "5";

    IVisio.Shape shape1 = page.DrawRectangle(1, 1, 2, 2);
}

 

The second improvement: No more casting to "short" with CellsSRC

Enums such as VisSectionIndices are ints. But CellSRC accepts shorts and this results in code always casting these values to type short. Now that is no longer necessary. And again this looks more like the code that Visio generates for macros. Another easy, cheap, win.

public static void sample1_c()
{
    IVisio.Application visapp = new Microsoft.Office.Interop.Visio.ApplicationClass();
    IVisio.Document doc = visapp.Documents.Add("");
    IVisio.Page page = doc.Pages[1];

    page.PageSheet.CellsSRC(
       
IVisio.VisSectionIndices.visSectionObject,
        IVisio.VisRowIndices.visRowPage,
        IVisio.VisCellIndices.visPageWidth
).Formula = "5";
    page.PageSheet.CellsSRC(
        IVisio.VisSectionIndices.visSectionObject,
        IVisio.VisRowIndices.visRowPage,
        IVisio.VisCellIndices.visPageHeight
).Formula = "5";

    IVisio.Shape shape1 = page.DrawRectangle(1, 1, 2, 2);
}

The third improvement: Much less typing and memory required to use CellsSRC

The problem with CellsSRC is that you always need three parameters and have get them correct. This has gotten much simpler in v3. The CellsSRC now takes an object that bundles all three together and a predefined list of these is available under the VisProps class.

So instead of remembering

IVisio.VisSectionIndices.visSectionObject, IVisio.VisRowIndices.visRowPage, IVisio.VisCellIndices.visPageWidth

You now just have to remember:

VisProps.PageWidth

And so the code gets much shorter.

public static void sample1_d()
{
    IVisio.Application visapp = new Microsoft.Office.Interop.Visio.ApplicationClass();
    IVisio.Document doc = visapp.Documents.Add("");
    IVisio.Page page = doc.Pages[1];

    page.PageSheet.CellsSRC( VisProps.PageWidth ).Formula = "5";
    page.PageSheet.CellsSRC( VisProps.PageHeight ).Formula = "5";

    IVisio.Shape shape1 = page.DrawRectangle(1, 1, 2, 2);
}

 

The fourth improvement: Convenience. Common tasks are easy available.

Extension methods have been added Visio classes that "just work". See how easy it is to set the page size?

public static void sample1_e()
{
    IVisio.Application visapp = new Microsoft.Office.Interop.Visio.ApplicationClass();
    IVisio.Document doc = visapp.Documents.Add("");
    IVisio.Page page = doc.Pages[1];

    page.SetSize(5, 5);

    IVisio.Shape shape1 = page.DrawRectangle(1, 1, 2, 2);
}

 

More convenience ...

You can just create the page with the correct size in the first place.

public static void sample1_f()
{
    IVisio.Application visapp = new Microsoft.Office.Interop.Visio.ApplicationClass();
    IVisio.Document doc = visapp.Documents.Add( new Isotope.Drawing.Size(5,5) );
    IVisio.Page page = doc.Pages[1];

    IVisio.Shape shape1 = page.DrawRectangle(1, 1, 2, 2);
}

 

The Diff

Red is what was removed. Light red means a removal occurred on that line.

Yellow is what was added. Light yellow means an addition occurred on that line.

image

 

 

How to start using the Library

Step 1- Create a new project in Visual Studio 2008 , Make sure to select .NET Framework 3.5

image

 

Step 2 - Add a reference to VIsio 2007 Primary Interop Assembly

Right-click on <project> / References  then select Add Reference

image

Select Microsoft.Office.Interop.Visio. Make sure you select Version 12, not 11

image

It will show in the references ...

image

 

 

 

Step 3 -Alias "IVisio" to Microsoft.Office.Interop.Visio

Add this line to the top part of the file "using IVisio = Microsoft.Office.Interop.Visio;"

image

 

Step 4. Add a reference to Isotope.Drawing.DLl and Isotope.Common.DLl, VisioAutomation.DLL

image

You'll see them in the references

image

Step 5. add using VisioAutomation

To use it effectively you must have a "using VisioAutomation" because that is how the extension methods get bound.

image

 

Let's compare, look at what Intellisense shows on Shape if you do not add this using line...

 

See After SetCenter there are just three methods before you see the ShapeAdded event.

 

image

 

Now when you add "using VisioAutomation" you'll see a all the extension methods are available.

 

image   

 

The Extension methods are clearly marked with the downward blue arrow

 

image

 

Now try it out...

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using IVisio = Microsoft.Office.Interop.Visio;
using VisioAutomation;

public class Demo

{

public static void sample1_f()
{
    IVisio.Application visapp = new Microsoft.Office.Interop.Visio.ApplicationClass();
    IVisio.Document doc = visapp.Documents.Add( new Isotope.Drawing.Size(5,5) );
    IVisio.Page page = doc.Pages[1];

    IVisio.Shape shape1 = page.DrawRectangle(1, 1, 2, 2);
}

}

 

Reference: Here's what VisProps contains