How can you access the CALLTHIS return value on the shapesheet?

Started by Hey Ken, November 06, 2015, 02:14:24 AM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Hey Ken

   Hey gurus!  Here's today's dilemma:

   I have a stencil with all sorts of macro-powered shapes (I call it my "Kencil").  All VBA code resides in the stencil, with no code in the drawing (a trick I learned in this forum).  Several macros are triggered by right-clicking the stencil's shape, and the right click action invokes the CALLTHIS shapesheet function to trigger the VBA magic.

   That works fine, except for one very user-unfriendly issue: If the drawing is opened without the stencil being present, whenever you right click on a shape all the macro-powered right click choices are still there.  But if you click on one of them, CALLTHIS calls nothing.  There's no stencil, and therefore no code to call.

   What I'd like to do is NOT display the macro-powered right click options at all if the code is not there, but display them when it is.

   The simple solution (if only it worked) would be to put NOT(CALLTHIS("AreYouThere?")) in each action's Invisible shapesheet cell.  The macro AreYouThere? would simply return "True", and the right click option would display.  But if the code is missing, nothing happens, and CALLTHIS defaults to returning "False", and the right-click option does not display.  And should you subsequently open the stencil, its code is suddenly there and the right-click choices all magically reappear. 

   That's the theory.  However, according to Microsoft, "The CALLTHIS function always evaluates to 0... Procedure can return a value, but Visio ignores it."  So it's always False no matter what.  Well, thanks for that, Mr. Gates. 

   So what do I do to automatically turn off the right click options in the absence of the supporting macros?  I've looked around, but have found nothing.  The only solution I can imagine is to add a document shapesheet cell, call it User.MacrosArePresent, to hold True or False, and set all the Invisible cells to NOT(TheDoc!User.MacrosArePresent).  Then, using the DocumentOpened event I can set User. MacrosArePresent to True, and DocumentClosed sets it to False.

   There's got to be a better way.

   - Ken

Ken V. Krawchuk
Author
No Dogs on Mars - A Starship Story
http://astarshipstory.com

wapperdude

You might try something like this in the action cell:

=SETF(GetRef(User.HaveMacro),"FALSE")+CALLTHIS("Macro1",)

Macro1 sets User.HaveMacro = "TRUE" if present, and does nothing if not, so the value remains "FALSE".

Then, based upon the User.HaveMacro value, either do your action or not. I think it takes a 2nd User.whatever to contain an IF statement to evaluate the true/false condition and execute or not something.

Wapperdude
Visio 2019 Pro

Visio Guy

Yeah, that's the only thing I can think off. Everything is OFF until a macro comes to life and goes around switching everything to ON.

Very much a hack.

I don't think that CALLTHIS has a return value that is interesting in anyway.

Another alternative is to write an Office/VSTO add-in in VB.NET or C#. I know that's not always an option, however.
For articles, tips and free content, see the Visio Guy Website at http://www.visguy.com
Get my Visio Book! Using Microsoft Visio 2010

wapperdude

As I think out loud about this, the user needs to know that the actions are not functional, or perhaps, the "false" condition triggers a call to fetch the macros AND runs the associated macro that would have been triggered had it been there.  So, each ACTION, needs a pair of User rows...one to store TRUE/FALSE, one to fetch the suite of macros plus run the corresponding action.

You cannot disable the actions /make them invisible, because once that happens, there's no obvious mechanism to revive...hmmmm, well, I suppose, you could hide them, and in the same step, unhide an action that puts up a menu item re "Macros unavailable, retry when installed"  Then once macros installed, click this new action to hide itself and reinstate the original actions.  Yeah.  That could work.

Wapperdude

Edit:  Updated Visio file, does work as anticipated!
Visio 2019 Pro

Hey Ken

Chris, Wapperdude:

   Thanks for the suggestions and samples, but those solutions each have their own issues that preclude their use, specifically:

1) No Office/VSTO add-ins, thanks.  Never used them, and I'd prefer not to invest the time in acquiring/learning just now, especially when I have a somewhat-workable Plan B in hand.

2) No right-click option saying "Macros unavailable, retry when installed", thanks.  I'd prefer to see no right-click options at all, not a suggestion that may be impossible to fulfill.  My Kencil-powered drawings get distributed far and wide, but not necessarily the Kencil.

   But Wapperdude's hack... Now that's a great idea I hadn't considered.  I started playing with it and quickly learned that unfortunately it left me with the same issue I had a few years back -- how to launch a macro from the shapesheet without the user clicking anything. 

   So I revisited that old problem and found there may actually be a possible solution when combined with the hack.  Did you know that if you put NOW() anywhere in any of the document's shapesheet user cells, it updates the cell when the document is first opened?  I never knew that.  So if you combine that trick with the hack, you could put the following in any document user cell:

=SETF(GetRef(User.HaveMacro),"FALSE")+CALLTHIS("Macro1",)+DEPENDSON(NOW())

   Open the document and it triggers just fine--except for one major showstopper: The SETF works, but the statement in Macro1 to set User.HaveMacro to True does absolutely nothing to the cell.  (As an unexpected side effect, attempting to update User.HaveMacro also re-triggers NOW(), and subsequently the macro; but adding a First-Time-Only boolean stops the macro from going into an infinite loop.  But I digress...)

   So I hacked at the hack, but to no avail.  Here's what I tried:

1) Putting NOW() into its own user cell and referencing that cell from the DEPENDSON.  Same result.
2) Resequencing the SETF, CALLTHIS, and DEPENDSON.  Same result.
3) Split the SETF and CALLTHIS into two different user cells.  Same result.
4) Moving it all to the page shapesheet.  Same result.
5) Added FORMULAFORCE to the macro.  Same result.
6) Set Application.EventsEnabled = False beforehand and True afterwards.  Same result.

   Bottom line: My Visio 2003 will not let the macro set the cell to anything at all if SETF first sets it to anything at all.  I added a line of code to set a second user cell, just to verify the macro was actually functioning, and that worked fine.  But the cell being SETFed simply refused to let the macro change it.

   So I guess I have to go to Plan B: using the DocumentOpened event to set User.HaveMacro True, and DocumentClosed to set it False.  That works fine, but the drawback is that I have to verify that events are still firing at the time I close the drawing, which is not necessarily guaranteed.

   So what did I miss?

   - Ken




   
Ken V. Krawchuk
Author
No Dogs on Mars - A Starship Story
http://astarshipstory.com

Yacine

Hi Ken,
I tried out Wapperdude's suggestion. In V2013 it works just fine.
So should V2003.

I noticed however that the procedure "CheckMacros" gets fired several times. V2003 may have a worse handling of these multiple calls (?).

Have a try with my files, they should work.
Yacine

Hey Ken

Yacine:

   Yes, Wapperdude's example worked for me too.  But it always had one action or the other displayed, and I want nothing displayed whenever the stencil is missing. 

   That said, I was able to make your example (thanks for that) work the way I want, but I had to first put your User.HideMenu into the action's Invisible cell.  That way if the macro was missing, so was the right click option.  All well and good so far, until I ran into the same problem from a few years back, namely, it can take up to a minute for NOW() to fire, so it can take up to a minute for the right-click actions to appear/disappear.  Call me picky, but I was looking for something a little more instantaneous.

   On the other hand, my Plan B is working well.  The stencil's MouseDown event sets TheDoc!User.HaveMacro to True; the stencil's BeforeDocumentClose event and the drawing's BeforeSave and BeforeSaveAs events set it False.  That way, whenever you close or open the drawing, or close the stencil, TheDoc!User.HaveMacro is set to False.  Clicking on the drawing sets it back to True whenever the stencil is open, or leaves it False when it's closed.  It's all instantaneous and covers all the bases, except for the infrequent times where the automation engine decides to stop working.  So unless someone knows of an instantaneous and autonomous solution to the triggering issue--or another approach entirely!--it looks I'll have to stick with Plan B for now.

   Thanks again to everyone for all the help.

   - Ken




Ken V. Krawchuk
Author
No Dogs on Mars - A Starship Story
http://astarshipstory.com

wapperdude

Seems like two basic issues.  One, how to trigger the hide action events, and two, how to make every shape respond?  A global, i.e., document based solution, e.g., your plan b, would seem to be universal.  Or, you build something into each shape, which has "triggering" issues.  The "hack" uses the "actions" as a trigger event.  But, that means, user interaction.  Perhaps, double click, on drop, whatever.  But, none of those are guarantee-able triggers.  It would seem that you either do something like Plan B, namely, global, or you do something like the "hack" which is a guaranteed trigger event.

Your global event speed might be increased if you try something like this to search for the stencil being present:
Sub Macro2()
    On Error Resume Next
    Set StnObj = Documents("KenTest.vss")
    If StnObj Is Nothing Then
        vsoShp.CellsU("User.HaveAct1").FormulaU = "False"
    Else
        vsoShp.CellsU("User.HaveAct1").FormulaU = "True"
    End If

End Sub


The lines for "False" and "True" would contain your desired response actions.  This code worked when triggered by RUNMACRO in the double click, action, and on drop shapesheet cells.  So, it ought to be adaptable to your global method.  Perhaps an approach might be you just write "TRUE" or "FALSE" into the desired cell in the shapes on the pages.  Then each shape either hides/shows the actions similar to the hack, but now you don't need any shape triggered events.

Wapperdude
Visio 2019 Pro

Hey Ken

Wapperdude:

   Yep, trigger and hide are the two basic issues.  Hide is easy; it's trigger that's tough, especially since it has to be done without user interaction, happen instantaneously, and apply to all pre-existing Kencil-powered shapes.  Plan B does that; nothing else I've yet seen does.

   Regarding your speedy Macro2, I have three observations:

1) Speed has never been an issue with any of my drawings (except for one time I mentioned a few months ago where I tried to get too fancy), and if we're looking for speed, we don't want to "just write TRUE or FALSE into the desired cell in the shapes on the pages", especially since some of my drawings run into hundreds of pages with hundreds of shapes per page;
2) Using the on-drop and double-click shape events as the trigger only addresses new shapes, not the pre-existing ones; and
3) Where would you put your Macro2??  All my code lives in the stencil; yours would have to live in the drawing.  Yes, I could use a template, but that would complicate my version update capabilities for existing drawings, not to mention violating my Prime Directive never to interfere with a user's locally-created macros.  Or am I missing something?
   
   - Ken
Ken V. Krawchuk
Author
No Dogs on Mars - A Starship Story
http://astarshipstory.com

wapperdude

Nope.  Didn't miss a thing.  You just expounded more than I did regarding some of the short comings.

You did mention a time issue wrt automation.  So, the last piece of code was offered as a possibility to circumvent that issue...namely, trying to get the stencil itself without using a stencil event.  But, as you noted, it would still take some code deployment, and that is a solution stopper.

It does seem like you still have to muck with the user's document to add the User.HaveMacro, and then have it disable the action events in each shape.  So that doesn't, ultimately, seem any more pristine than the "hack"...which only changes the shapes in question and doesn't add an event capture to the stencil nor one to the document, both of which require dynamically changing the
shapes every time the document is opened and closed.  Plus, the User has no clue that the actions are missing because the stencil is missing.  If that is an intentional deployment option, then the hack warning may be a useful reminder and only shows if the User tries to use actions, but doesn't impose any additional, waste of time steps...say, e.g., dismissing the annoying warning before being allowed to proceed.

That's my two cents worth regarding the merits of the two approaches.  You,  obviously, have to choose what works best and have a more complete view.  I only offer the above as something to consider...definitely not saying one approach is better than the other, so please don't take it that way. 

The important thing...you have a solution that works and is satisfactory.

Wapperdude
Visio 2019 Pro

Hey Ken

   I should learn.

   Yacine warned me that "the procedure "CheckMacros" gets fired several times", and even I noticed "an unexpected side effect: attempting to update User.HaveMacro also re-triggers NOW(), and subsequently the macro".  But rather than tracking down the root cause, I left it with "adding a First-Time-Only boolean stops the macro from going into an infinite loop."

   It's a situation right out of the Wizard of Oz where the Cowardly Lion complains, "Somebody pulled my tail!", and the Scarecrow replies, "Oh, you did it yourself!", because it turns out there is no infinite loop.  Yes, the macro does fire numerous times; but at the end of all the firings, in every case the flag ends up set exactly as it should be.  So when I went and added my First-Time-Only boolean, I stopped the update from doing its thing.  That was me pulling my own tail, as well as standing on my own foot.  Goes to show.

   Thanks again to everyone for all the help.  Plan B has been in effect for a week now, and we've encountered no case where it fails.  "A solution that works and is satisfactory," as Wapperdude so aptly put it.  Doesn't get any better than that.

   - Ken




   


Ken V. Krawchuk
Author
No Dogs on Mars - A Starship Story
http://astarshipstory.com