Code to call a macro when a shape is deleted

Started by droper1, November 08, 2024, 05:37:21 PM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

droper1

I'm trying to get VBA to call a macro when a shape or selection of shapes is deleted from a page. The macro in question is providing a real time count of the different shapes on the page so we can see at a glance how much of a piece of equipment is needed. I've been able to get this to work when ADDING shapes to a page, but can't seem to figure out how to make it work when deleting. Using the below code, the count will always be 1 off when deleting because the macro is called BEFORE the selection is deleted. I don't see any other functions on the list that would trigger when a shape is deleted, though. Any insight would be appreciated. My coding ability is limited, and ChatGPT can only get me so far...

Private Sub Document_ShapeAdded(ByVal Shape As IVShape)
    ' Update the count display when a shape is added
    Call Display10pipecountInTextBox
    Call Display20pipecountInTextBox
End Sub

Private Sub Document_BeforeSelectionDelete(ByVal Selection As IVSelection)
' Update the count display when a shape is added
    Call Display10pipecountInTextBox
    Call Display20pipecountInTextBox
End Sub

wapperdude

Officially, there is no event for after deleting.  Don't recall seeing a workaround either.
Visio 2019 Pro

Yacine

#2
You're correct in observing that the BeforeSelectionDelete event triggers before the selected shapes are deleted. This can make it challenging if you're trying to process or count shapes, as the deleted shapes are technically still available at that point.

The main purpose of the BeforeSelectionDelete event is to give you an opportunity to intervene before the actual deletion happens—whether that means modifying, counting, or even canceling the deletion. In this particular case, since you don't want to prevent the deletion but rather just update your counters accurately, a small tweak is needed.

Here's a clearer breakdown of the solution:

Delete the Shapes First: To get the correct count, you need to ensure the shapes are actually removed before calling your counter macro.
Update the Count Afterwards: Once the shapes are deleted, you can then accurately update the count.
Here's how you can modify your code:

Private Sub Document_BeforeSelectionDelete(ByVal Selection As IVSelection)
    ' Delete the selected shapes first
    Selection.Delete
   
    ' Now update the count display after deletion
    Call Display10pipecountInTextBox
    Call Display20pipecountInTextBox
End Sub
Yacine

droper1

It seems that using selection.delete throws a run-time error, since everything in this code should be happening prior to the shape being deleted. I even tried setting up a separate macro with selection.delete that the original code calls prior to calling the other macros, but that did not work either. I tried to work through some other options with chatgpt, and it has recommended using the AddAdvise function. The code it wrote is throwing an overflow error though, and I believe there must be a syntax error in the code.

Sub InitializeEventHandlers()
    Dim evt As Visio.Event
    Dim i As Integer

    ' Loop backwards through the EventList to delete each event individually
    With ActiveDocument.EventList
        For i = .Count To 1 Step -1
            .Item(i).Delete
        Next i
    End With

    ' Add an event for shape addition
    Set evt = ActiveDocument.EventList.AddAdvise(visEvtAdd + visEvtShape, _
                visActCodeRunMacro, "Display10pipecountInTextBox", "")

    ' Add an event for shape deletion
    Set evt = ActiveDocument.EventList.AddAdvise(visEvtDel + visEvtShape, _
                visActCodeRunMacro, "Display10pipecountInTextBox", "")
End Sub

(edit: error is being highlighted at the "Set evt = ActiveDocument..." portion of the code)

droper1

I've figured out an imperfect work around. I created a simple macro that saves the document, then used that as a trigger to call the correct macros after the shape or shapes have been deleted. Code below:

Private Sub Document_BeforeSelectionDelete(ByVal Selection As IVSelection)
    ' Save Document to trigger count macros
    Call SaveDocument
End Sub

Private Sub Document_DocumentSaved(ByVal doc As IVDocument)
    Call Display10pipecountInTextBox
    Call Display20pipecountInTextBox
End Sub

Private Sub Document_ShapeAdded(ByVal Shape As IVShape)
    ' Update the count display when a shape is added
    Call Display10pipecountInTextBox
    Call Display20pipecountInTextBox
End Sub

This works, but unsure if it will bog down larger files when it has to save every time a shape is deleted.

Nikolay

#5
If you call selection.Delete from onBeforeSelectionDeleted event, this is stack overflow, correct. I.e. "selection.Delete" will call your "before" event handler again. And then event handler will call selection.Delete. And so on. Just don't do that, but simply do your stuff in the event handler.

The shapes will be deleted by Visio anyway, this event is just a notification for your app, there is no need to delete shapes with code

If you save a document every time a selection is deleted, your users will most probably be not happy.
Not sure what this workaround is supposed to do?

Yacine

Quote from: Nikolay on November 11, 2024, 03:46:03 PMIf you call selection.Delete from onBeforeSelectionDeleted event, this is stack overflow, correct. I.e. "selection.Delete" will call your "before" event handler again. And then event handler will call selection.Delete. And so on. Just don't do that, but simply do your stuff in the event handler.
Of course! How embarrassing, I told you non-sense. Nikolay is right.
On the other hand, you want to update the number of shapes to delete, so you may modify your counting routines and add selection.count as parameter.
Yacine

Nikolay

There could be a concern, that the delete operation may be actually cancelled for some shapes after the "before" handler is called (if they are protected from deletion, for example). But calling ".delete" inside does not sound right.

In fact, if a shape is protected from deletion and cannot be deleted, it is excluded from the Selection object that is passed in (i.e. the "selection" passed in seems to contain only shapes that will be actually deleted). The event that happens before to give an opportunity to ask for a permission or abandon delete is QueryCancelSelectionDelete, that is probably the one you must have mixed with?

Browser ID: smf (possibly_robot)
Templates: 4: index (default), Display (default), GenericControls (default), GenericControls (default).
Sub templates: 6: init, html_above, body_above, main, body_below, html_below.
Language files: 4: index+Modifications.english (default), Post.english (default), Editor.english (default), Drafts.english (default).
Style sheets: 4: index.css, attachments.css, jquery.sceditor.css, responsive.css.
Hooks called: 291 (show)
Files included: 34 - 1306KB. (show)
Memory used: 1145KB.
Tokens: post-login.
Cache hits: 13: 0.00178s for 26,597 bytes (show)
Cache misses: 2: (show)
Queries used: 16.

[Show Queries]