Ending undo transaction in macro in PowerPoint - vba

As described in http://support.microsoft.com/kb/278591 PowerPoint will usually combine all changes that a macro or add-in makes to a single undo step. So if you put the following code into VBA and execute it twice by pressing F5 there
will only be one undo step.
Sub Move()
If ActiveWindow.Selection.Type = ppSelectionShapes Then
ActiveWindow.Selection.ShapeRange.IncrementLeft 10
End If
End Sub
I am looking for a way to change this behavior in a more complex scenario where the user can make multiple changes without directly accessing PowerPoint. Ideally, it should be possible to undo a set of modifications that corresponds to one change from the user's perspective.
What I found out is that ExecuteMso seems to break the undo transaction. So if you execute the following code twice it results in 4 undo steps (first increment, ExecuteMso, second increment, ExecuteMso).
Sub Move()
If ActiveWindow.Selection.Type = ppSelectionShapes Then
ActiveWindow.Selection.ShapeRange.IncrementLeft 10
Application.CommandBars.ExecuteMso "Bold"
End If
End Sub
Anyone knows a real solution for the problem or a better workaround? Although I didn't find it maybe there is a solution for Word or Excel that can be ported to PowerPoint?

If you replace the ExecuteMso line with this one, the entire procedure remains one Undo:
ActiveWindow.Selection.ShapeRange.TextEffect.FontBold = msoTrue
Update
If you are using PowerPoint 2010 or later put this line before each block of code that you want the user to be able to undo a step at a time:
Application.StartNewUndoEntry

Related

Is it possible to trigger some event when a comment is added or deleted in MS Word using VBA?

In MS Word, when adding a comment to a text selection, I would like to change the background color of this selection if the comment contains some "keyword".
Then obviously, if the comment is deleted or doesn't contain the "keyword", I need to remove this color/highlight to the scope text.
I managed to do it on the global 'run macro' with this code (except the remove part), but ideally this should be dynamic and activates when the focus changes back from the comment to the document or anything else.
Here is a simplified version of my code attempt so far:
'
' ColorComments Macro
'
Dim wdCmt As Comment, cat As String, pg As Paragraph
For Each pg In ActiveDocument.Paragraphs
'Trying to reset the background colors before reapplying the active ones but doesn't work
pg.Shading.BackgroundPatternColorIndex = 0
Next
For Each wdCmt In ActiveDocument.Comments
With wdCmt
If Trim(.Range) = "keyword" Then
'Probably a better way to change the background color of the text that would give me access to more colors?
.Scope.Shading.BackgroundPatternColorIndex = 2
Else
'resetting the comment "Scope" color if it doesn't contain the code
.Scope.Shading.BackgroundPatternColorIndex = 0
End If
End With
Next
End Sub
I guess I should use the pulic events or the WindowsSelectionChange event but I can't find a documentation about it anywhere.
I've little to no experience in VBA so thanks in advance for awy help :)
To make that work effectively you will need to repurpose these two controls from the RibbonUI:
ReviewNewComment
ReviewDeleteComment
Here are a couple of links to articles on repurposing RibbonUI controls
https://gregmaxey.com/word_tip_pages/repurpose_user_interface_controls.html
https://www.experts-exchange.com/articles/21499/Intercepting-Office-Ribbon-Control-Events-with-VBA-using-Repurposing-Commands.html
In prior versions of Word, I don’t recall seeing what version you are trying to use, VBA solutions could access the following event routine:
Sub InsertNewComment()
'
' InsertNewComment Macro
' Insert comment (includes menu)
'
End Sub
And even though this routine still works if you invoke it directly from VBA, this is not what is used from the RibbonUI in today’s versions of Word. I know that because I’ve tried to trap the event using the subroutine but haven’t been successful.
The following VBA routine is invoked by clicking on the Insert Ink Comment on the RibbonUI.
Sub InsertInkComment()
'
' InsertInkComment Macro
' Insert ink comment
'
Selection.Comments.Add Range:=Selection.Range
End Sub
It’s a mystery why this one works and the other does not and that is why I said upfront that you will have to trap and repurpose the RibbonUI, particularly for regular comments.
As for trapping the Delete Comment event, there isn’t a DeleteComment VBA routine that I have found. There are event routines for Deleting All Comments but none for a single comment.

Worksheet_Change Event not firing

My Excel project functions properly at home (with Excel 2010), but not on two work computers (with Excel 2016) and I suspect the Worksheet_Change event is the problem.
When the user makes changes, the yellow bar (in the screenshot) should turn white again, but it is not. I am getting 2 different responses on 2 work computers.
Two things to point out in the code:
In some places I use vbColor extensions, in others I had to use a numerical code.
One computer is not firing the Worksheet_Change event at all. I would note that the change event is at the top of the code, although that shouldn't have anything to do with it.
I'd appreciate advice and detailed explanations, to help me learn.
Private Sub Worksheet_Change(ByVal Target As Range) 'Check for On-Time and Delays then change the Command Button Colors to show completed.
'Return headers to white after jump to
Range("B3:I3,O3:V3,B28:I28,O28:V28,B53:I53,O53:V53,B78:I78,O78:V78,B103:I103,O103:V103,B128:I128,O128:V128,B153:I153,O153:V153").Interior.Color = vbWhite
'Check for On Time and Delayed Trips
'Trip 1 Scan Ready
If IsEmpty(Range("L3").Value) = False Then
If Range("L3").Value > Range("I3").Value Then 'If actual is greater than Departure
'If Delayed check for a delay code
If IsEmpty(Range("L24").Value) Then 'If Delay code is missing
Range("K24:L25").Interior.Color = 16711935
CommandButton1.BackColor = 16711935
CommandButton1.ForeColor = vbBlack
Else 'If Delay Code is present check for delay time
If IsEmpty(Range("L25").Value) Then
Range("K24:L25").Interior.Color.Index = 16711935
CommandButton1.BackColor = 16711935
CommandButton1.ForeColor = vbBlack
Else
CommandButton1.BackColor = vbRed
CommandButton1.ForeColor = vbWhite
Range("K24:L25").Interior.Color = vbWhite
End If
End If
Else
'Flight was on Time
CommandButton1.BackColor = 32768 '32768 = Green
CommandButton1.ForeColor = vbWhite
Range("K24:L25").Interior.Color = vbWhite
End If
End If
There could be a number of factors causing this problem. One way to diagnose is to troubleshoot like this:
At the beginning of your procedure, right after this line:
Private Sub Worksheet_Change(ByVal Target As Range)
...add a temporary line:
MsgBox "Changed: " & Target.Address
...then go change something in your worksheet (whatever change isn't firing the event as you'd expect).
One of two things will happen:
You'll have a message box pop up, showing the cell reference of whatever was just changed.
This demonstrates that the event is firing properly, so the issue must be in your code that follows.
Or, you won't get a message box pop up. This indicates the event is not firing, which could be caused by a few possibilities:
Are macros completely disabled in the workbook? This is often done automatically on workbooks received from outside sources. Save the workbook to a trusted location on the local computer or network (rather than opening from the email). Do other sections of code run properly? When you close/re-open the file, are you given a warning about Macro Security? Also, try rebooting the computer.
Other security settings could be an issue. Have you ever run VBA on these machines? You can confirm sure code is able to run in Excels' security settings in:
File→Options→Trust Center→Trust Center Settings→Macro Settings
As well as making sure macros are enabled there, you could also check Trusted Locations in the Trust Center, and either save your document in a listed location, or add a new location. Security settings will be "reduced" for documents saved in those locations.
Is EnableEvents being intentionally disabled elsewhere in your code? If you wrote all the code, you should know whether you set EnableEvents = False at some point. Perhaps it was intentional, but it's not being re-enabled.
Remember to remove the line you added temporarily, or that MsgBox will quickly get annoying by popping up every time a change is made. :)
You say "the change event is at the top of the code". A worksheet change event will only fire if you put the code in the sheet module concerned. If you've put the code concerned in a non sheet module (e.g. "Module 1" or similar, listed under the "Modules" branch in the object explorer) then that's the problem.
Also, you really shouldn't hard-code cell references like "L3" in your VBA code, because every hard reference will require amending should you (or a user) later insert rows/columns above/to the left of these references. Instead, assign meaningful named ranges to these cells back in Excel, and use those in your VBA.
Also, when using event handlers like you're doing, you should have something like If not intersect(Target, InputRange) is nothing then... so that the code only runs if something of interest changes.
I had the same problem.
I checked everything.
Everything seemed to be proper (enabled macros, EnableEvents=True, etc).
I closed and opened Excel.
Problem persisted.
There was nothing I could do.
I restarted Windows.
Problem disappeared.
Restart took 7 minutes (with all applications closing & restarting), trying to find the cause would take much more. Maybe I could have tried to find & kill every Excel process in Task Manager.
I don't like giving people the advice "try rebooting", but well, Windows is Windows.
I had a different problem. I had saved my worksheet under a new name and then added the code for the event. To get it to work, I had to close the worksheet and reopen it. It then showed the button to enable macros and the code started to work. Hope this helps someone.
Wade
I solved it simply deactivating "Design Mode" in the "Developer" tab.
It seems if you are in "Design Mode", this event (I don't know about others) won't work.
In 2019 Excel cut copy -> paste would not work the paste options were grayed out. Online sources said update and repair the app. I updated office and the copy/paste options worked.
However the worksheet_change event stopped working. After a lot debugging with no luck, I did a hail mary and commented out the subroutine and recreated it. It WORKED!! I have absolutely no idea why or how. If anyone has thoughts please share.
I had some code in a worksheet change that wasnt firing. I also had some code in Thisworkbook upon opening and saving. Resolved the issue by putting Application.EnableEvents = True at the end of the code for workbook open and workbook save.

VBA macro for simulating Ctrl+click on a bookmark/hyperlink

I'm trying to write a VBA macro in Microsoft Word to do the same thing as Ctrl+click does (follow a link or go to the bookmark).
I've tried SendKeys but I don't think that works for left mouse click.
I've actually came up with a partially working solution involving the use of
Selection.GoTo What:=wdGoToBookmark, Name:=BLAbut this unfortunately means I can't use ctrl+< because it seems that the history of where the cursor previously was is not saved.
So instead of coming up with my own solution, is there actually a way to just bind the action of Ctrl+click to another button? Or is there a way to write a macro that'll do the same action including keeping track of the history of the cursor?
The following code should do what you want. Install it on a standard code module.
Option Explicit
Dim ReturnRange As Range
Sub GotoBookmark()
' 13 Sep 2017
With Selection
If .Hyperlinks.Count Then
Set ReturnRange = .Range
.Hyperlinks(1).Follow
End If
End With
End Sub
Sub ReturnToLink()
' 13 Sep 2017
If Not ReturnRange Is Nothing Then ReturnRange.Select
End Sub
For testing purposes, create a bookmark in your document and a hyperlink to it. Select the hyperlink and run Sub GotoBookmark. Then run procedure ReturnToLink to go back to where you came from. Note that you can return from anywhere as well as multiple times.
You may wish to create keyboard shortcuts to call the two subs.

Troubleshooting Large Macro for Word 2016

I am trying to create a macro for Word 2016 that will automatically edit a document based on a list of about 250 search terms that I used the "find and replace" function to change. When I record the macro from start to finish and attempt to run, I receive a message saying "Procedure is too long". I have looked through answers on here and understand that you can clean up some code and/or create sub sections within the code to troubleshoot. However, I am not sure what I am doing and the code is very long.
Could someone help me please?
portion of code is included below:
Portion of code
Break your macro into steps. The easiest way is (use a copy of your document for testing, so you can always put the original text back if you make a mistake!):
Record a short macro. It doesn't matter what it does. Name it something like DoAllReplacements.
Record a macro that does your first block of replacements. Name it something like DoReplacement1.
View the macros, and edit the DoReplacement1 macro. Copy all of the text from the end of the comment block to the line just before End Sub.
Put the edit caret (cursor) at the end of the DoReplacement1 End Sub and hit enter twice. Enter the following text:
Sub DoReplacement2()
End Sub
Paste the code from the clipboard between the Sub and End Sub lines you just added. Edit the macro code to do your next block of replacements.
Repeat the above until you have all of your replacements coded, using as many new DoReplacementX routines as needed.
Go back to the very first macro you recorded (DoAllReplacements), and remove all of the code between the Sub DoAllReplacements() and End Sub. Add new calls to the individual DoReplacementX macros you wrote.
Sub DoAllReplacements()
DoReplacement1
DoReplacement2
DoReplacement3
' Etc. until you've added them all
End Sub
Exit the macros code window. Use View Macros, select your DoAllReplacements macro and execute it.

Save undo stack during macro run

I am wondering if there is a way to save ability to undo actions after macro has been run.
I do not care about results of macro - just need to undo actions that were done by user before macro.
Background:
I have a macro on the worksheet_change event that logs who and when made the change on this worksheet. I do not want it to restrict user's ability to undo his/her actions.
There is no easy way to do this, but it's possible. The approach to this is to create three macros, and use some global variables to save state:
MyMacro
MyStateSavingMacro
MyStateRevertingMacro
E.g. My macro changes Cells in Range A1:A10 of the active sheet. So, whenever the code to run my macro is called, it executes
Sub MyMacro()
Call MyStateSavingMacro()
' Copies contents and formulae in range A1:A10 to a global data object
'... Code for MyMacro goes here
'
'................
Call Application.OnUndo("Undo MyMacro", "MyStateRevertingMacro")
'This puts MyStateRevertingMacro() in the Undo queue
'So pressing ctrl-Z invokes code in that procedure
End Sub
Sub MyStateSavingMacro()
' Code to copy into global data structures anything you might change
End Sub
Sub MyStateRevertingMacro
' Code to copy onto the spreadsheet the original state stored in the global variables
End Sub
So there it is. It's not pretty, but can be done.
Ref: http://msdn.microsoft.com/en-us/library/office/ff194135%28v=office.15%29.aspx
Edit:
To preserve the Undo queue prior to your MyMacro being run, the inelegant solution would be to create a chain of 4-5 MyStateRevertingMacro_1, _2, etc. where you can apply the information from your Worksheet_Change logging system and then chain-up the Application.OnUndo in each of those, so Application.OnUndo for each of those Reverting Macros would refer the previous state reversion code.
You can use the hidden mirror sheet to do this. Of course it will work if your worksheet is simple enough. You must decide which cells are editable and copy them TO mirror sheet, which are not editable and copy them FROM mirror sheet. And your macro should work only in the mirror sheet. That's it.
This one is a bit old now but in case anyone is still trying to get this to work - I just tried setting the undo stack to be able undo the formatting on a column that a macro had reformatted and noticed that by doing that the full undo command worked (before I added this bit the undo was unavailable) - does not matter what the custom undo code contains (in my case I had not even created the routine yet), the application undo works perfectly
Application.OnUndo "Undo Amount Format", "sUndo_Col2"