VSTO Word 2016: Squiggly underline without affecting undo
Asked Answered
R

2

13

I am working on a real-time language analysis tool that needs to highlight words to draw attention from the writer in Word 2016 using a VSTO add-in, written in .NET4.6.1 with C#. Think of the grammar/spelling check, which adds a squiggly line underneath a word to show you that the word has grammatical or spelling errors. I'm adding a similar feature for some of my own defined rules.

I searched around on adding squiggly lines, and stumbled on Font.Underline and Font.UnderlineColor. I set this on the range of a word, and it appears to provided that visual stumili I was after to draw attention. There is a problem, though. Every underline I add or underline color I change adds an undo action to the undo stack.

I don't want this to happen, or I want a way to pop the action I just did in code from the stack. The aim is to have the user be able to use CTRL+Z to remove text he changed, and not affect my language anlysis result.

How would I go about doing this?

Raffin answered 27/11, 2016 at 13:14 Comment(0)
F
6

I stumbled on this question, which is asking exactly the same thing: Prevent actions to be added to the word undo redo OR Remove actions from the undo redo CommandBarComboBox

As @Manu pointed out that same question was also asked over at MSDN where the answer was:

The UndoClear method will empty the list. That's the best you can do.

There is also a similar question on here Word VSTO override CTRL+Z / CTRL+Y which suggests the Microsoft.Office.Interop.Word.UndoRecord route or MessageHooks in AddIns.


After more research I noticed a great idea at the end of this thread: MSDN Draw my own squigglies on Word document where you keep track of your actions and then skip past them in Undo and Redo operations.

Here is an excellent example of code to do "transactional undo/redo's" Can I create an undo transaction in Word or Excel? (VSTO) . You can do this same method in VSTO except for one big problem, as noted by Dirk Vollmar in his answer:

I don't think that overwriting built-in Word commands is possible using VSTO alone, though

I have overwritten some built-in commands in VSTO using keyboard hooking events to intercept commands: How to perform .Onkey Event in an Excel Add-In created with Visual Studio 2010? However I'm not sure if you can recreate the Ribbon to intercept button commands. More specifically, the Undo and Redo are built-in galleries in the Ribbon UI and you can do nothing with a built-in Ribbon gallery. And in versions like 2010 the Undo/Redo buttons are in the title bar - and you cannot add/edit buttons on the title bar using VSTO:

enter image description here

So if you're concerned with trapping the button commands (everyone I know uses Ctrl+Z & Y), you might inject VBA code to get access to EditUndo and EditRedo events, eg:

VB._VBComponent vbModule = VBProj.VBE.ActiveVBProject.VBComponents.Add(VB.vbext_ComponentType.vbext_ct_StdModule);
String functionText = "Public Sub EditUndo() \n";
functionText += "MsgBox \"Undo Happened\"\n";
functionText += "End Sub";
vbModule.CodeModule.AddFromString(functionText);

Main problem with this approach is Trust needs to be granted.


Another answer in this same QA Can I create an undo transaction in Word or Excel? (VSTO) is by Mike Regan who answered 3 months after Dirk. He used a hidden document and placed it in the real document when needed to make any amount of VSTO actions a single undo.

Still doesn't solve the problem of preventing an action being recorded in the Undo History.


I did try a Registry key to limit the UndoHistory to 0 and reset it back to 100 (in order to disable the History while adding an action), but it appears its only for Excel https://support.microsoft.com/en-gb/kb/211922

There maybe an undocumented reg key to disable the Undo/Redo history altogether but it would only be read by Word on startup. I thought the UndoHistory key containing a number would be read before each Undo/Redo, but no luck with this approach at all.


It's not an easy problem to solve there are big limitations, so it might be easier to:

a) Accept that your spell/grammer checker Add-In is included in the Undo/Redo list (defeat).

b) Work out where the line/text is on screen and show a transparent tooltip highlighting the problem. This is a lot harder than it seems and is less than ideal, here are two great answers to guide you on this method: Detecting text changes in Word 2016 from VSTO add-in or a much simpler approach to detect XY positions from this Microsoft email thread: https://groups.google.com/forum/#!topic/microsoft.public.word.vba.general/pKq4PsqD3cM

//position dialog relative to word insertion point (caret)
int left = 0;
int top = 0;
int width = 0;
int height = 0;
MSWord.Range r = Globals.ThisDocument.Application.Selection.Range;
MSWord.Window w = Globals.ThisDocument.ActiveWindow;

w.GetPoint(out left, out top, out width, out height, r);

frmPopUp newForm = new frmPopUp();
newForm.SetDesktopLocation( left + width + 2, top - newForm.Height + height );

c) Only trap Undo/Redo events by the Keyboard with Transactional Undo/Redo's and let users see the atomic Undo/Redo's using the buttons. It would be extremely dodgy to remove the Undo/Redo buttons and that will cause heaps of Where's my Undo button gone? support cases. So don't do this, if you're curious, quick access toolobar customization can be done from ".qat" files. Ref: https://support.microsoft.com/en-us/kb/926805

d) Use a Mouse Hook and detect when the Undo/Redo buttons are clicked. Here is the code to hook up the mouse, note this wont play nice with corporate Anti-Virus products and I dont recommend it: https://blogs.msdn.microsoft.com/andreww/2009/02/24/message-hooks-in-add-ins/ or https://github.com/gmamaladze/globalmousekeyhook.

e) Try to intercept raw windows messages, eg Excel CustomTaskPane with WebBrowser control - keyboard/focus issues just beware as per my comment referencing BUG: Cant choose dates on a DatePicker that fall outside a floating VSTO Add-In that message pumps in Office sometimes exhibit weird behaviour.

Freberg answered 5/12, 2016 at 2:45 Comment(0)
D
3

Unfortunately, it doesn't look like there is an easy solution to this problem. From what I've seen, you have two options:

  1. You could clear the entire undo stack using Document.UndoClear. However, this won't be very user friendly, since all previous actions aren't in the undo list anymore.
  2. Work with the UndoRecord class. Between your calls to StartCustomRecord and EndCustomRecord, all actions in your code will only generate one item in the undo stack. CTRL+Z will still affect your language analysis, but the whole undo stack won't be as polluted as before.

I know this isn't what you want, but even Microsoft MVPs don't have a better solution.

Dinodinoflagellate answered 1/12, 2016 at 14:41 Comment(1)
Thanks! This vastly improves the current situation.Raffin

© 2022 - 2024 — McMap. All rights reserved.