Visio Automation

Microsoft Office products have the ability to be integrated with Visual Studio in what is called “Automation.” I view this technique as a dumb and dirty capability that I use for extracting data from Word, Excel, Visio or other Office applications. In this example I’m going to show a method to read some basic information from Visio. MS Visio is a drawing tool. This is different from a paint-like tool, in that, the objects drawing (circles, rectangles, lines, text, etc.) remain as distinct objects that can be resized, rotated and modified after they have been positioned. MS Visio is a great tool for doing flow charts and diagrams. It can also be used to create simple maps or floor plans.

Let’s say that you want to create a floor plan and you want to read the x,y coordinates of each line defined in a visio diagram so you can store them in your program for use in an SVG viewer application. First, you need to create a file with your floor plan. Here’s my sample floor plan:

OK, now we need to create a blank project in Visual Studio. Just create a new forms application and drop a button on the front of the form. I titled my button “Read Plan”, but you can make up any name you want, or just leave it on the default. I also added a multi-line text box (use the text box object, and change the attribute to multi-line):

Remember, this is just a quick and dirty program to read the map data. We’re the only one to use this program. If you are planning to build a program that will be used by other people, you’ll need to clean this up and put in a file-picker to choose the file to import.

OK, so now you need to include the using statement:

using Microsoft.Office.Interop.Visio;

Add the Microsoft.Office.Interop.Visio assembly to the references of the project. Then use the following code to read the end points of the lines:

private void btnFloorPlan_Click(object sender, EventArgs e)
{
    // I stored the visio file in a temp folder on the C: drive
    string docPath = @"c:tempfloor_plan.vsd";
    Microsoft.Office.Interop.Visio.Application app = new 
        Microsoft.Office.Interop.Visio.Application();

    Document doc = app.Documents.Open(docPath);

    // read items from the first page
    Page page = doc.Pages[1];

    foreach (Shape shape in page.Shapes)
    {
        double lnStartX = shape.Cells["BeginX"].ResultIU;
        double lnStartY = shape.Cells["BeginY"].ResultIU;
        double lnEndX = shape.Cells["EndX"].ResultIU;
        double lnEndY = shape.Cells["EndY"].ResultIU;

        txtResults.Text += 
            lnStartX.ToString("0.00") + "," + 
            lnStartY.ToString("0.00") + "," + 
            lnEndX.ToString("0.00") + "," + 
            lnEndY.ToString("0.00") + "rn";
    }

    app.Quit();
}

OK, so now we have some code and it opens the Visio file and reads each shape on page 1. In my example, the Visio file only contains lines, so when I run it, I expect to read only lines and I don’t need to verify which shapes are being read. I just read the end points. You’ll notice when you run the app, that Visio starts up (and yes you need Visio installed to make this code work). The last line, “app.Quit();” causes Visio to exit. If you don’t close the Visio application, then a new copy will start up if you run the program a second time.

Now you can run the program and you should get some numbers (not necessarily the same numbers displayed here):

Now you can select the text and copy to the clipboard. I created a web application with a Default.aspx web page and deleted all the text (except the first line) from the front page:

Next, I wrote an empty method in the code behind:

protected void Page_Load(object sender, EventArgs e)
{
    RenderFloorPlan();
}
 
private void RenderFloorPlan()
{
 
}

And now I paste in the text from the Visio reader application, formatted the information to fit into a list of strings and came up with this result:

private void RenderFloorPlan()
{
    List<string> laLines = new List<string>() {
        "2.00,10.00,6.00,10.00",
        "2.00,10.00,2.00,7.00",
        "6.00,10.00,6.00,7.00",
        "2.00,7.00,6.00,7.00",
        "3.00,10.00,3.00,9.50",
        "2.00,9.50,2.50,9.50",
        "2.75,9.50,3.25,9.50",
        "3.50,9.50,4.25,9.50",
        "4.00,9.50,4.00,10.00",
        "4.50,9.50,5.25,9.50",
        "5.00,9.50,5.00,10.00",
        "5.50,9.50,6.00,9.50",
        "2.00,8.00,2.25,8.00",
        "2.50,8.00,3.50,8.00",
        "3.75,8.00,4.00,8.00",
        "4.00,7.00,4.00,8.00"};
}

You can format the output to include quotes and commas if you have a really big list and don’t want to do it manually. That can save some time.

Now, we need to do something with this list. First, I’m going to setup a generic SVG html output, right after the list defined above:

Response.Write("<!DOCTYPE html>");
Response.Write("<html>");
Response.Write("<body>");
Response.Write("<svg id='SVGObject' xmlns='http://www.w3.org/2000/svg' version='1.1'>");

// content goes here
         
Response.Write("</svg>");
Response.Write("</body>");
Response.Write("</html>");

Now we need to put something in that spot that says “content goes here”. That’s where we loop through the list and print the lines. I’m going to warn you up front, that the coordinates that are read from the Visio drawing are not in the same scale as the SVG interface. So you’ll need to play with a multiplier on each coordinate to get your diagram to fit the area on the screen that you want. So make sure you set this up so you can do that:

for (var i = 0; i < laLines.Count; i++) 
{ 
    string[] lsLineCoords = laLines[i].Split(',');
    double lnStartX = Convert.ToDouble(lsLineCoords[0]) * 50; 
    double lnStartY = Convert.ToDouble(lsLineCoords[1]) * 50;
    double lnEndX = Convert.ToDouble(lsLineCoords[2]) * 50;
    double lnEndY = Convert.ToDouble(lsLineCoords[3]) * 50;
    Response.Write("<line x1='" + lnStartX + "' y1='" + lnStartY + 
        "' x2='" + lnEndX + "' y2='" + lnEndY + 
        "' style='strokewidth:1px;stroke:black;' />");
}

Now run the program. You should see something like this rendered in your browser:

Now you’re probably wondering “why is it upside down?” There’s a reason for that and it’s easy to fix. First, Visio has an origin in the bottom left, so all coordinates go from bottom to top and left to right. SVG has an origin in the top left corner. So SVG’s coordinates go from top to bottom and left to right. All we need to do to fix this situation is subtract our “Y” coordinates from the height of the display area:

for (var i = 0; i < laLines.Count; i++)
{
     string[] lsLineCoords = laLines[i].Split(',');
     double lnStartX = Convert.ToDouble(lsLineCoords[0]) * 50;
     double lnStartY = 600 - Convert.ToDouble(lsLineCoords[1]) * 50;
     double lnEndX = Convert.ToDouble(lsLineCoords[2]) * 50;
     double lnEndY = 600 - Convert.ToDouble(lsLineCoords[3]) * 50;
     Response.Write("<line x1='" + lnStartX + "' y1='" + lnStartY + 
         "' x2='" + lnEndX + "' y2='" + lnEndY + 
         "' style='strokewidth:1px;stroke:black;' />");
}

I chose 600 just to make sure it fit on the browser. You can add or subtract a constant from the x and y coordinates to shift the image on the screen. Here’s the final result:

Ta-da!

The whole purpose of this example is to show how to use some basic MS Visio automation in Visual Studio and then use the data read to render something in SVG to match the Visio image.

If you find any errors or have something to add, please leave a comment.

Leave a Reply