Viewer OCX control VERY slow

G

Gary Shell

I am building an application that takes data from a database and creates a
Visio Document using the CrossFunctional Flowchart Template. It draws 20 or
more "swim lanes", places process blocks into appropriate lanes and add
lines to the left and right of each process block.

As the drawing progresses, I must periodically do Application.DoEvents to
see the progress. As the drawing completes it gets slower and slower and
slower.

I ran a profiler on the code and found that the code that actually creates
the drawing makes up a small amount of the total execution time. Total
execution time before I killed the app was 34853 seconds. The Profiler
shows my drawing code module named "crowsfeet" as consuming a total of 1010
seconds! I am assuming that points to the Visio OCX itself as taking all
the time.

I have a feeling this may be due to the template having code that is running
and every little cell change my code does causes the template code to
re-execute. So I am wondering if there is a way to suspend the template
code from executing constantly and have it do it's thing ONCE each time I do
my Application.DoEvents to refresh my view.

I remember in Excel there was/is a way to tell it to turn off calculations,
make some series of edits and then turn calculations back on. Can, and
SHOULD, Visio be told to do this in my case?

Or am I dealing with crossing the boundary between managed and unmanaged
code? And if so how can I mitigate this.

At the rate the app was going a complex drawing will take a couple of hours
to generate.

Gary
 
G

Gary Shell

Sorry, I forgot this was not a .Net specific group. My app is in VB.Net and
the OCX is the interop tool from Visio 2003.

Gary
 
C

Chris Roth [ Visio MVP ]

Yes, you're fighting a Visio-shipped add-on code. Some things to try:

DropMany - figure out who and where to drop, then do it all at once.
SetMany - set lots of cells/formulas at once.

VisioIsIdle - wait unitl Visio is done screwing around, or being screwed
around with before you do anything.

Filter events - set flags that indicate that YOUR code is doing something,
or isn't doing something (like positioning a shape), so you can ignore as
much junk possible.

More tips from Mail-lan's blog, including turning off various recalcs. Not
sure how this will effect the SwimLane add-on...

Top Five Peformance Tips
http://blogs.msdn.com/mailant/archive/2004/09/22/233082.aspx

Top Ten Things to Know When Using the Visio 2003 ActiveX Control
http://blogs.msdn.com/mailant/archive/2004/09/24/233928.aspx

--

Hope this helps,

Chris Roth
Visio MVP
 
G

Gary Shell

Thanks Chris,

I checked out the stuff regarding DeferRecalc and am confused by something.
Here is the help info from MSDN on DeferRecalc:

-------------------------------------------
Use the DeferRecalc property to improve performance during a series of
actions. For example, you can defer formula recalculation while changing the
formulas or values of several cells. When the series of actions is complete,
you must always set the DeferRecalc property back to the value it had before
you changed it. See the following examples.

If you release objects or send a large number of commands to Visio while
recalculation is deferred, Visio may at times need to process its queue of
pending recalculations. Because of this, use care in setting formulas inside
a scope where you want recalculation deferred. Ideally, you should only set
formulas when recalculation is turned off.

For example, consider the following Microsoft Visual Basic for Applications
(VBA) sequence:

Dim blsDeferCalcOriginalValue As Boolean
blsDeferCalcOriginalValue = Application.DeferRecalc
Application.DeferRecalc = True
vsoShape.Cells("height").ResultIU = 12
vsoShape.Cells("width").ResultIU = 14
Application.DeferRecalc = blsDeferCalcOriginalValue
Because VBA makes and releases a temporary Cell object in the preceding
code, Visio will process its queue at that point.

In the following sequence, Visio will not process the recalculation queue
until the application turns recalculation on again (or the user performs
some operation).

Dim blsDeferCalcOriginalValue As Boolean
blsDeferCalcOriginalValue = Application.DeferRecalc
Application.DeferRecalc = True
Set vsoCell1 = vsoShape.Cells("Height")
Set vsoCell2 = vsoShape.Cells("Width")
vsoCell1.ResultIU = 12
vsoCell2.ResultIU = 14
Application.DeferRecalc = blsDeferCalcOriginalValue
___________________________I am not following why the first example will
make and release a temporary cell and the second example does not.

Gary
 
G

Gary Shell

Chris,

Another related issue. I instrumented some of my code in an attempt to see
where the bottle neck actually occurred. I originally had code that used to
put a gradient on the process blocks, but I had read some tine ago the
admonishment NOT to use gradients when using the interop OCX. I had changed
that code to use a FillPatern of 1 to avoid the gradient issue. It occurred
to me I had NO IDEA what fill pattern the CrossFunctional flowchart template
was using as a default for my processes prior to my forcing them to 1. So,
I commented out my setting the FIllPattern and found, sure enough, the
template was using a gradient!

I'm willing to bet that is the source of my trouble. So I thought I'd go to
the code where I instantiated a mastershape from the template and set the
fill pattern in that master before it's placed on the page.

mastObj = stnBasicFlow.Masters(strMasterShape)
mastobj.cells("FillPattern").ResultIU=1
shpObj = pagobj.Drop(mastObj, 0, 0)

But it doesn't seem that I can do this:
mastobj.cells("FillPattern").ResultIU=1 a master object has no cells
collection.

I found the reference to SetFormulas in the Visio code reference. I think
that's what I'd need to use to modify the master. But I can't understand the
example it provided. Can you shed some light? Actually I set several
parameters to the shape AFTER it's dropped on the page that I could easily
do to the master once and be done with it. (That assumes I can reuse this
master over and over again, which my code CURRENTLY does not do, but if I am
interpreting things correctly it SHOULD be.)

Thanks again,
Gary
 
M

Mark Nelson [MS]

In the second example, all objects are explicitly defined. The Cells method
on the Shape object needs to return a Cell object. The second example
supplies a variable for the Cell object, so that no new variables need to be
created.

--
Mark Nelson
Microsoft Corporation


This posting is provided "AS IS" with no warranties, and confers no rights.
 
M

Mark Nelson [MS]

There are a couple things to be aware of when working with masters. First,
a master is a container for one or more shapes. That means to access the
contents of a master, you need to get a Shape object within the master
first.

shpObj = mastObj.Shapes(1)
shpObj.Cells("FillPattern").ResultIU = 1

Second, masters require extra steps to modify their contents. The code
above works for reading properties from masters but not for making changes
to masters. A master object must be opened as a copy. Changes are made to
the copy. Then the copy is closed, updating the original master and its
shape instances on the page. Look at the Open method for masters in the
help for a detail explanation.

mastCopyObj = mastObj.Open
shpObj = mastCopyObj.Shapes(1)
shpObj.Cells("FillPattern").ResultIU = 1
mastCopyObj.Close

--
Mark Nelson
Microsoft Corporation

This posting is provided "AS IS" with no warranties, and confers no rights.
 
G

Gary Shell

Mark,

First thanks very much to you and Chris. This thread has been MOST
enlightening.

I was dropping about 160 shapes onto the form and then changing about a
dozen properties of each shape. Of those 12 properties, ten of them were
the same for each shape! I now realized that one thing I could do to
dramatically improve the performance was to create one shape from the
master, modify the ten properties and then drop instances of that modified
shape on the form and modify just the two parameters that varied for each
shape.

That made a HUGE difference in performance and I didn't have to modify the
master shape at all.

Each process shape also gets a dozen or more input and output lines
associated with it. Each line is actually made up of three line segments.
I am going to apply this same technique of building a dummy shape (line with
all three segments), set all the common properties once and then drop this
dummy shape (line) on the page as needed. This will cut the number of trips
across the managed to unmanaged boundary MANY MANY times. Probably two
orders of magnitude difference!!!

Thanks again,
Gary
 
M

Mark Nelson [MS]

Good progress. Yes, when working with automation you always want to
minimize the number of calls. Even with unmanaged code in process, Visio's
automation layer is much slower than the native app.

Second, in any large drawing it is critical to minimize the number of shapes
you work with. Visio shapes are incredibly flexible and powerful, and that
capability comes at the expense of file size and memory. On average a shape
in Visio consumes 7KB of memory. Also groups with sub-shapes are
particularly expensive for performance. Every sub-shape must go through a
transform to compute its position and geometry in terms of the drawing page.
It is much cheaper to put multiple geometry sections in a single shape than
to have multiple shapes with simple geometry.

Third, it can be a big help to use masters. If you plan to have more than
two instances of a shape, make a master for them. Shapes that derive from
masters are much more efficient than masterless shapes.

--
Mark Nelson
Microsoft Corporation

This posting is provided "AS IS" with no warranties, and confers no rights.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top