Monday
Oct202008

Visio Automation: Three Hello World Samples – C#, F#, and IronPython

 

Periodically I get a “how do I get started?” question about using automation Visio. So, in the interests of sharing, I’ll show 3 simple “Hello World” examples that you can build on for more complex tasks.

All these samples do the same thing: launch Visio 2007, create a new doc, and draw a rectangle with the text “Hello World”. Below is a sample of the output.

image

C# (Visual Studio 2008)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
// First Add a reference to the Visio Primary Interop Assembly:
//  In the "Solution Explorer", right click on "References", select "Add Reference"
//  The "Add Reference" dialog will launch
//  then in the ".NET" Tab select "Microsoft.Office.Interop.Visio"
//  Click "OK"
using IVisio = Microsoft.Office.Interop.Visio;
namespace Visio2007AutomationHelloWorldCSharp
{
    class Program
    {
        static void Main(string[] args)
        {
            IVisio.ApplicationClass visapp = new IVisio.ApplicationClass();
            IVisio.Document doc = visapp.Documents.Add("");
            IVisio.Page page = visapp.ActivePage;
            IVisio.Shape shape = page.DrawRectangle(1, 1, 5, 4);
            shape.Text = "Hello World";
        }
    }
}

F# (Visual Studio 2008)

#light
// Step 1 - Add a reference to the Visio Primary Interop Assembly
//  In the "Solution Explorer", right click on "References", select "Add Reference"
//  The "Add Reference" dialog will launch
//  then in the ".NET" Tab select "Microsoft.Office.Interop.Visio"
//  Click "OK"
let visapp = new Microsoft.Office.Interop.Visio.ApplicationClass()
let doc = visapp.Documents.Add("")
let page = visapp.ActivePage;
let shape = page.DrawRectangle(1.0, 1.0, 5.0, 4.0)
shape.Text <-  "Hello World";

IronPython (IronPython 2.0 Beta 4)

import sys
import clr
import System
clr.AddReference("Microsoft.Office.Interop.Visio")
import Microsoft.Office.Interop.Visio
IVisio = Microsoft.Office.Interop.Visio
visapp = IVisio.ApplicationClass()
doc = visapp.Documents.Add("")
page = visapp.ActivePage
shape = page.DrawRectangle(1, 1, 5, 4)
shape.Text = "Hello World"
Wednesday
Sep032008

Notes on Using Client Applications to Generate Server-Side Graphics

Occasionally I am contacted by someone who wants to generate some graphics content on a web server and they want to use a client application like Visio or PowerPoint or Photoshop as as an engine to generate those images dynamically on a web server. They are often looking for some guidance on whether this is a better approach than, for example, manually creating the images using custom code.

The fundamental issue is that client applications are (usually) never designed to be run in this scenario.

Typical issues one will encounter

  • Simultaneous requests – A single instance of a client applications will often be unable to handle multiple client requests at the same time. You may have to spawn multiple instances or queue the requests. (I’ll leave identifying any threading issues as an exercise to the reader)
  • Security – You’ve got to be careful to examine the potential risk of running a client application in a server context. For example, can a client use malformed input to cause the application to do something that would put the server at risk? You need to think about the threat model for this scenario.
  • Interactivity – Client apps tend to assume that there is a human sitting in front of a monitor using the app and so will show UI (modal dialogs, etc.). When there is an interactive user, this is no problem, the user handles or dismisses the UI and continues. On the server side, you may find that the client app will try to launch some interactive UI and will either outright fail or simply block waiting on user input. A typical example are the “File already exists. Do you want to overwrite?” dialogs. You’ll have to code around these issues if you can.
  • Licensing – The license under which one uses the software may explicitly or implicitly restrict or forbid its use as a server-side application.
  • API support – Some client apps allow a user to interactively create content but does not provide an API to do so programmatically. Sometimes if the API is exposed, you may find that the API is quite limited compared to what an interactive user can create.
  • Performance and Memory Usage – you may find that even low numbers of requests per unit time that the client application consumes too much memory or CPU cycles.

My Recommendations

  • (a) Roll-your own.  XML-based vector formats like SVG or Silverlight might be good candidates here because it is simple to create the content.
  • (b) Re-use an existing library that is built for generating server side graphics (example: libgd)

Even with this recommendation one will have to consider most the issues I’ve identified earlier, but at least one will have more control over those issues.

Update [2008-08-09]: Additional links

Monday
Apr142008

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.

Friday
Feb152008

Visio Trick: One-Color and Two-Color Glows

In which shapes will receive a spotlight

Let's suppose you have the network diagram below.

image

And what you'd like to to provide a little extra attention on server A. For example to emphasize it's importance or status. Perhaps we wish to indicate it's in a "warning" state.

You could try setting the fill color ...

image

And that would work. But given the nice 3-point gradient used in the diagram, we want to go the extra mile and make highlight A in a more subtle manner. What we want is this:

image

And now we'll learn how to make a "glow" shape that you can use to highlight other shapes.

First, draw a rectangle, and right-click and select Format / Fill

image

The Fill dialog launches

image

for Pattern select 40 ( a radial fill(

image

Then for Color select Orange

image

Then for Fill Pattern select Orange

image

Then click OK.

image

Enable the shapesheet by going to Tools/Options/Advanced and selecting run in developer mode

image

Right click on Show Shapesheet and in the Fill Format section, change the FillBkgndTrans to 100%.

image

The shape will now look like this:

image

Get rid of the line ...

image

And now the shape will look like this:

image

Now place it above the background but underneath the server A shape and you get:

image

At this point, you've got a nice highlight effect. But we can do better. Leveraging the technique for the 3-point gradients, we are going to make a two color glow.

To help visualize the differences:

image

In the 2-color glow, we have the same orange color, but toward the center it gets progressively brighter to white.

NOTE: the 2-color glow is still 1 shape.

How to achieve this?

First, draw a 1-color glow using the color white.

image

Select that shape and choose Format / Shadow

image

In the Shadow dialog

  • Set Style to 13: Offset, custom
  • Set Color to orange (or whatever you want)
  • Set Pattern to 40
  • Set Pattern Color to orange (or whatever you selected for Color)
  • Set the X Offset & Y Offset to 0 and 0
  • Set Magnification to 170%

And close the Shadow dialog

You should see this:

image

Right click on the shape and select Show Shapesheet

And in the Fill Format section set ShdwBkgndTrans to 100%

image

And you will now have this

image

Which you can then use as a more intense highlight for Server A

image

Notice boundary of the shape.

Keep in mind that the orange glow is beyond the shape's selection area and is thus not selectable. You'll have to click on the "inner" glow. The picture below should make it clear.

image

Also, you can have fun with the glows to make some nice backgrounds.

image

A visio file with all the shapes is attached to this post.

Thursday
Feb072008

Visio 2007 Trick: 3-Point Gradient Fills with Transparency

A Question

How many shapes are required to draw the image below in Visio?

image

The Answer

5 shapes.

No groups, no wierd geometries. Just 5 shapes.

What I want

I want rich, smooth, multi-color gradient fills with independent transparencies for each color.

I could get what I want by drawing multiple shapes. That can work. But, sometimes it's irritating. The shapes have to be perfectly aligned, you'll have some selection wierdness, etc. Simpler to have 1 shape.

What is a  3-Point Gradient Fill with Transparency?

Before I show the steps. Let me give you a clearer understanding of what I mean.

First, here is a conceptual drawing the 3-point I really want to draw:

image

Now, without getting into the explanation, the gradient we'll be able to draw will be more like this:

image

So visualize it forming like this:

image

Implementing 3-Point Gradient Fill with Transparency

We are going to use a combination of the normal shape fills and the SHADOW feature to draw a 3-point gradient.

It's not perfect, it doesn't do everything you'd expect in an application like Illustrator, but I'm sure it's more than what you've seen with Visio so far.

Just so that the goal is clear: here is what we will end-up with:

image

  • ORANGE in the upper left
  • LIGHT BLUE in the upper-right
  • DARK BLUE in along the bottom

Steps

  • Launch Visio
  • Create a new document
  • Draw a rectangle

image

  • Select the rectangle, right-click, and choose Format / Fill...

image

  • The Fill dialog will appear

image

  • Set the colors appropriately (pay attention)
  • Set the Fill / Pattern to 36

image

  • Set Fill / Color and FIll / Pattern Color to the color you want for the upper left (ORANGE)

image

  • Don't touch the transparency for now
  • Set the Shadow / Pattern to 28

image

  • Set Shadow / Color to the color you want along the bottom of the shape (DARK BLUE)
  • Set Shadow / Pattern Color to the color you want at the upper right of the shape (LIGHT BLUE)
  • Click OK
  • Here is what you have now

image

  • Turn on the shape sheet via Tools / Options / Advanced / Run in developer mode and click OK to close the Tools / Options dialog

image

  • Select the shape, right click, and select Show ShapeSheet
  • Find the FIllBkgndTrans cell and change the value from 0% to 100%

image

  • You'll notice the change in the shape once you finish making this change

image

  • close the shapesheet window

image

  • A closer look

image

  • Select the shape
  • Form the menu, select Format / Shadow ...

image

  • The Shadow dialog launches

image

  • Under the Size & Position section, click the black dot in the middle of all the arrows

image

  • A close-up of the black dot to click

image

  • Once you click the dot, the Shadow dialog will look like this

image

  • NOTE: when you click on the black dot, the Shadow / Style changed to "13: Offset, custom" (this is expected)
  • Click OK to close the Shadow dialog
  • What we have created is a single shape with a three-point gradient.

image

  • If you edit the fill and shadow transparencies, you can vary the transparencies as needed

That was hard, how can I create another one?

  • Just duplicate the object and edit the colors in the Fill dialog to get what you want.

What about an existing shape? How can I copy the effect?

  • Use the format painter button

image

How do I create the picture are the beginning of the post?

image

  • Duplicate this shape 4 times for a total of five shapes.
  • Resize and stack three on top of each other and modify the colors via the FIll dialog.
  • Make the other two into vertical columns, set the colors and the transparencies
  • Play with the patterns and transparencies. You'll get some nice combinations!

Summary

  • A single shape that avoids selection weirdness and keep the file size manageable
  • An easy way to change the colors
  • Trivial to generate a different gradients, you only need to use the shapesheet the first time: Create this shape once, save it as a file, if you ever want another gradient just reload, duplicate, change colors via the UI as desired.