SETATREF(Height) / EventXFMod Behavior

Started by MacGyver, April 23, 2024, 04:19:25 PM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Visio Guy

#15
Without reading the post carefully, there's a way to handle this: your function ConnPtAdjust does one little thing:

bStuffChanged = TRUE

You might also want to note the shape's id, so you can find it later...

collChangedIds.Add(id)

...'later' happens when Visio.Application.NoEventsPending or  Visio.Application.IsIdle fire off. I can never remember which is the best event. When one of these fires, you process your que of IDs if bStuffChanged is true, and update your shapes.

This allows Visio to "settle down" after a flurry of events. So it matters less that your event is firing many, many times.

You'll have to figure out how to pass the id to your procedure. CALLTHIS allows for some arguments.

For articles, tips and free content, see the Visio Guy Website at http://www.visguy.com
Get my Visio Book! Using Microsoft Visio 2010

Yacine

#16
@Chris, I saw the IsIdle property too, but the help said it gets only fired if you are listening to other events. Didn't understand and dropped this path.

But, and that is funny, I saw your answer in a google forum twenty years back and this inspired me to the following solution.

We don't need to handle the last event, but ONE event. So let it be the first one and block for a certain period all the following events.
We use a flag "Busy" to destroy the unwanted events, go in a loop with a doevents to prevent blocking other stuff and that's it.

Dim bBusy As Boolean
Dim counter As Long
Dim rejected As Long

Sub ConnPtAdjust(shp As Visio.Shape)
  Dim start As Double
  If bBusy Then
'    Debug.Print "Reject event"
      rejected = rejected + 1
    Exit Sub
  Else
    rejected = 0
    bBusy = True
    start = Timer
    counter = counter + 1
    Debug.Print "Start time: ", start
    While Timer - start < 1
      DoEvents
    Wend
    Debug.Print "Executing now @", Timer, "repetition", counter, "Rejected events", rejected
    actualRoutine shp
    bBusy = False
  End If
End Sub

Sub actualRoutine(shp As Visio.Shape)
  ' do stuff
End sub

Yacine

Thomas Winkel

#17
I removed the feedback-loop (User.Processing) and fixed a bug in AddRow (rowNew +1).
See attached file.
Is the behavior now as desired?

Edit:
Basically this is the same approach as Yacines or as I described above.
But this involves ShapeSheet technique to prevent calling the macro.

wapperdude

#18
@Thomas W: 
QuoteIs the behavior now as desired?
Sorry.  Does not eliminate the multiple firings, nor the addition of extraneous row entries.

Above reply was edited to make more sense.
Visio 2019 Pro

wapperdude

#19
@Yacine:  Sleep well.  Works great.  I copied the core of the OP's code to the "actualRoutine" placeholder, and conn Pts get added or removed as necessary.

Here's the complete solution...

Dim bBusy As Boolean
Dim counter As Long
Dim rejected As Long

Sub ConnPtAdjust(shp As Visio.Shape)
  Dim start As Double
  If bBusy Then
'    Debug.Print "Reject event"
    rejected = rejected + 1
    Exit Sub
  Else
    rejected = 0
    bBusy = True
    start = Timer
    counter = counter + 1
    Debug.Print "Start time: ", start
    While Timer - start < 1
      DoEvents
    Wend
    Debug.Print "Executing now @", Timer, "repetition", counter, "Rejected events", rejected
    actualRoutine shp
    bBusy = False
  End If
End Sub

Sub actualRoutine(shp As Visio.Shape)
    connPtRow = shp.RowCount(visSectionConnectionPts) - 1   ' rownum used in vba formula generation is 0 based
    lastCPYVal = shp.CellsSRC(visSectionConnectionPts, connPtRow, visCnnctY).Result(visInches)

    If lastCPYVal >= 0.875 Then
'        Debug.Print "Add rows"
        rowsToAdd = 1 + Int((lastCPYVal - 0.875) / 0.625)
        rowNew = connPtRow
        ' we add rowsToAdd*2 cuz we add two conn points (left/right side of shape) per elevation

        For i = 1 To rowsToAdd
'            Debug.Print "Adding " & shp.Name & " Point " & rowNew

            ' left side conn point
            rowNew = rowNew + 1
'            Debug.Print "setting data"
            shp.AddRow visSectionConnectionPts, rowNew, visCnnctX
            shp.CellsSRC(visSectionConnectionPts, rowNew, visCnnctX).FormulaForceU = "Connections.X3"
            shp.CellsSRC(visSectionConnectionPts, rowNew, visCnnctY).FormulaForceU = "Connections.Y" & rowNew & "-0.625"
            shp.CellsSRC(visSectionConnectionPts, rowNew, visCnnctDirX).FormulaU = "Connections.DirX[3]"
            ' right side conn point
            rowNew = rowNew + 1
            shp.AddRow visSectionConnectionPts, rowNew, visCnnctX
            shp.CellsSRC(visSectionConnectionPts, rowNew, visCnnctX).FormulaForceU = "Connections.X4"
            shp.CellsSRC(visSectionConnectionPts, rowNew, visCnnctY).FormulaForceU = "Connections.Y" & rowNew
            shp.CellsSRC(visSectionConnectionPts, rowNew, visCnnctDirX).FormulaU = "Connections.DirX[4]"
        Next

    ' need to remove connection points
    ElseIf lastCPYVal < 0.375 Then
        For i = connPtRow To 1 Step -1 ' start removing conn points from last row, decreasing by two (left/right side of shape)
            If shp.CellsSRC(visSectionConnectionPts, i, visCnnctY).Result(visInches) < 0.375 Then
'                Debug.Print "Removing"; shp.Name & " Point " & i & ", " & shp.CellsSRC(visSectionConnectionPts, i, visCnnctY).Result(visInches)
                shp.DeleteRow visSectionConnectionPts, i
            Else
                Exit For
            End If
        Next

    End If
End Sub



Visio 2019 Pro

Yacine

Quote from: Thomas Winkel on April 26, 2024, 06:59:13 PMBasically this is the same approach as Yacines or as I described above.
But this involves ShapeSheet technique to prevent calling the macro.

I do also favor shapesheet solutions over macros. Didn't however get your solution to work.
Yacine

Thomas Winkel

#21
Agree that the (ShapeSheet only) solution in my first post only works stable if the called code does not trigger more than one event.
But see the demo I provided with PreventFeedbackLoops.vsdm (0 Downloads).
Then replace this cell:
EventXFMod=IF(User.Processing,SETF(GetRef(User.Processing),FALSE),SETF(GetRef(User.Processing),TRUE)+CALLTHIS("DoSomething"))
with:
EventXFMod=CALLTHIS("DoSomething")
And you will see that this results in an endless-loop until it crashes.

MacGyvers code does not crash (but results in unpredictable behavior) because it saturates.
(I.e. comes to a state where no further events are triggered by the code.)

Did you test "tester.vsdm" I attached above? I'm quite sure that this works.
It's the same as your solution.
Only I store bBusy in the shape and not in a public variable.
That allows me to access it in the ShapeSheet before I call the code:
=IF(User.bBusy,"",CALLTHIS("ConnPtAdjust"))

It's been a long time and I cannot remember the exact background.
But I think that the solution with a public variable did not work for me in some situations.
Imagine many shapes trigger the same event at a time (e.g. user copies many shapes at once).
Then each shape requires its own "bBusy" state.

wapperdude

@Thomas W.:  In my previous reply, that was in response to the latest file version you had provided.  This multiple triggering is not due to code instability.  I had no issues with the OP's code in that regards.  The code always came to a normal conclusion...provided that there was no shape corruption.  I'll explain below. 

The multiple triggerings that are experienced are a normal response to using a mouse to drag-resize the shape, whether it be to enlarge or reduce the size.  As the dragging to a new size progresses, Visio will send multiple event signals.  Each of these will re-trigger the code, potentially causing excessive growth.  Occassionally, this causes the row removal portion of the algorithm to fail.  It is an abnormal condition / response.  Generally, though, the code reacts OK.  Unfortunately, there is no shapesheet solution applicable to the goals of the desired task of adding / removing connection point rows in response to height changes, multiple triggers notwithstanding.

Enter Yacine latest code solution.  It allows and waits until the multiple triggers have completed before continuing with the actual row count modifications.  Thus, there is only a single edit sequence applied to the shape based upon the final (and stable) size due to dragging.

Additionally, the OP's code did execute without issue if, for example, a new height value is entered via Size N Position window.  Visio only issues a single event response.  The code executed just fine.

Visio 2019 Pro

wapperdude

#23
Quote@VisioGuy: ...'later' happens when Visio.Application.NoEventsPending or  Visio.Application.IsIdle fire off.
Stumbled across your old post...preferred seems to be VisioIsIdle.


Quote@Yacine:  @Chris, I saw the IsIdle property too, but the help said it gets only fired if you are listening to other events. Didn't understand and dropped this path.

@VisioGuy, Yacine:  Despite my curiosity, I've only managed to kill a bunch of cats with no satisfaction of bringing them back.  Alas, I can't get no satisfaction!!! 

I'm trying to understand how the VisioIsIdle event is called and utilized in VBA application.  It would seem like an obvious, alternative solution for this repeated event triggering issue.  But, the mechanics of it's implementation escape me.  I'm getting increasingly dull in my advancing age progression.  As Yacine's solution works well, this is an intellectual conundrum.  Rescue me!  Won't someone please rescue me!?! 

Well, life goes on...
Visio 2019 Pro

Yacine

#24
Quote from: wapperdude on April 27, 2024, 03:40:55 PMthis is an intellectual conundrum.  Rescue me!  Won't someone please rescue me!?!
Well, life goes on...

I don't want to rescue you, I'd rather jump in next to you. All this event handling in Visio is so cryptic, with Add and Addadvice, Sink so and so. I am somehow missing a good overview article.
The regular Microsoft help pages are quite poor in this regard.
Has someone seen a good article somewhere?
Yacine

wapperdude

@Yacine:  For sure!!!  Everything you said is SOOOO true.  Nice to see I'm not alone concerning this. 

Perhaps...to paraphrase a S & G song...Where have you gone VisioGuy? The Forum turns its lonely eyes to you...". 😁😄😄. Potential, patented, major topic development discussing how & why, the ins / outs, and examples regarding this topic of handling repeated event firings.

@VisioGuy:  in your abundant spare time, of course!  😉😊
Visio 2019 Pro

Thomas Winkel

@wapperdude:
Ah, I see what you mean, I must be blind :-[
Now I've spend an hour investigating and only get bullshit results that makes no sense.
Anyhow, I still think that all the events come either from the called VBA code and / or from the ShapeSheet formulas that calculate & set the height to fit a grid.
(The latter could be solved either by using the BOUND function, or by setting the new height in the same VBA function.)

wapperdude

#27
Quote from: Thomas Winkel on April 27, 2024, 09:39:08 PMAh, I see what you mean, I must be blind :-[
Oh how I wish I could say I have perfect vision.

Quote from: Thomas Winkel on April 27, 2024, 09:39:08 PMAnyhow, I still think that all the events come either from the called VBA code and / or from the ShapeSheet formulas that calculate & set the height to fit a grid.
I'm quite sure it's not code, barring any unusual problems.  With Yacine's code, it's possible to debug.print each time the code is called, and that's before the OP's code (or any other "actualRoutine") is executed.  I suppose doing Event Monitor ought to verify the repeated calls from the mouse dragging.

I doubt that a bound fcn might eliminate this affect.  The OP already does the equivalent with a SETF formula.  Additionally, I deleted the SETF formula and grid snapping, and encountered increased multiple events.  Finally, placed a traditional, functionally equivalent SETEATREF formula in the height cell itself.  There may be a slight performance improvement, but still saw multiple events.  Seems to be the nature of resizing via mouse drag. The appropriate solution is for Visio to restrain sending any sizing event until after mouse release.  Well, that brings us back to Yacine's approach or possibly VisioIsIdle, which I don't understand, and may be more complicated anyways.

It's possible that the multiple events are related to screen redraw as the dragging takes place.  Just a thought.

Visio 2019 Pro

wapperdude

Found the issue.  It is linked to re-drawing...

Go to File>Options>Advanced.  Deselect Enable live dynamics.  That halts shape re-drawing, which halts events associated with dragging. 

Visio 2019 Pro

Thomas Winkel

Quote from: wapperdude on April 28, 2024, 06:29:35 AMDeselect Enable live dynamics
Makes perfect sense because I had live dynamics disabled already.
So unexpected events only occurred sometimes (probably triggered by User.HeightCalc).