none
How can I best implement the missing Word Application event DocumentAfterSave RRS feed

  • Question

  • I have a class which adds some KeyBindings to all documents contained within the project.  These keybindings need to be removed before the document is saved and then readded once the document has been saved

    currently i am handling all of this within the DocumentBeforeSave event, whereby i remove the keybindings, save the document and then readd the keybindings.  

    but, and this is the problem, i then set the Cancel parameter to true to prevent the Save dialog possibly appearing again.  this is all well and good if no other addins handle the DocumentBeforeSave event, or override the vba SaveFile, SaveFileAs etc functions, but causes failure's if they do.  

    So, i need to be able to handle a DocumentAfterSave event so that i can simply remove the keybindings in the DocumentBeforeSave eventhandler, and then re-add them in the DocumentAfterSave eventhandler.

    Can somebody give me some pointers about this, and some best practises about cancelling out of, and overriding Words inbuilt application events and functions, because to me it appears that this is not a good programming practise and wont play nicely with other addins

    cheers

    m

    Wednesday, October 3, 2012 9:59 AM

Answers

  • Hi Matt

    Thank you for taking the time to provide so much detail :-)

    <<this function was originally written in VBA and i am currently converting it to c# dot>>

    You are aware that you can't use KeyBindings with non-VBA code? So you'd need to at least keep the macros that run for the delete and backspace keys in VBA. Or you'd need to work with Windows API keyboard hooks to get away from VBA completely. I figured it best to mention this, first...

    <<the need to remove the keybindings before the document is saved is to prevent the delete and backspace keys from failing to function correctly if the document is transefered to another computer where the project addin is not installed.>>

    Mmm. I've got an idea and I'm going to do some "thinking out loud" - but I'm not sure of the logic until it all comes out, so bear with me :-)

    Theoretically, you could provide the user with a "link up the keybindings again" button for when the keybindings would get removed, but not reattached (DocumentClose being cancelled). "Undo" can save an indavertant deletion of a break. The two could be combined in an "emergency" button. Then only the SaveAs and Close events are critical; Save, not really.

    Conflicts, however, remain an issue. So I ask myself whether it could make sense to take the "remove keybindings" out of the add-in. Instead, have your add-in monitor DocumentOpen and NewDocument, which can't be cancelled. Maintain a list of document paths and names that have been opened. Monitor SaveAs to get "new" file names". Monitor DocumentBeforeClose to track when any of these may get closed.

    It's easy enough to determine whether there's a file lock on a document, which will tell you whether it was actually closed. And to monitor the Application.Documents collection to check whether a file is still in the list.

    So, at latest when the add-in unloads you should know which documents had been opened and need to have the keybindings removed. The add-in could then trigger a tool that works with these files using the Open XML file format - meaning the work isn't done within the Word application, but on the "closed" files. This can loop through the list it's been keeping of opened documents and remove the keybindings. It should be fairly fast as there's no interaction with the Word application.

    The only "gotcha" I can see here is if, between the time the document is closed and the add-in unloaded, the document is sent to / accessed by someone who doesn't have the add-in?

    The other thought that comes to mind is to make sure the keybindings are in the template attached to the document. That would mean that NO keybindings are linked to the document, itself.


    Cindy Meister, VSTO/Word MVP, my blog

    Thursday, October 4, 2012 2:35 PM
    Moderator
  • Hi Matt

    <<secondly, i cant provide the relink button as documents are passed to users who definitely wont have any custom functionality installed, and so wouldn't have this tool either.>>

    You misunderstood my intention :-) The relink functionality would be for the user who may have done something (cancelled close) while working with your tool that removed the keybindings. Just a simple way to re-instate them. IOW the liklihood is higher that the user working with the tool might lose the keybindings and anyone opening the document after that would be less likely to have keybindings.

    <<your last suggestion mite well be the winner here... as i haven't tried that.  and i think that's by far the easiest to test>>

    Yes, absolutely, if it works for you :-)

    <<ultimately, it would appear that i havent programmed this functionality too well in the first place, and whether that's because i'm a bad programmer or the object model has enabled me to adopt bad practices, i'm not sure.  what would you think?>>

    That you're running into design limitations dicated by the way the Office applications were conceived, more than twenty years ago :-) Back then, the idea was to make life easier for the user. But the possibilities provided were so powerful, that they lend themselves to much more - and scenarios that weren't considered when the APIs were designed. I've no idea whether you're a "good" or "bad" programmer - but almost everyone hits these issues when "file management" situations come up.

    <<although having not gone anywhere near the Open XML format yet, i'd need to get very dirty in order to achieve this!>>

    Actually, it probably wouldn't be all that complex. The Keybindings are stored in their own "part" so it would simply be a matter of deleting that, and the entry for its Relationship. Same approach as removing VBA from a document, just a different Part.

    <<the potential problem i see here is, what if the documents are saved in a location that my fancy KeyBindingsCleanerMonitor class cannot locate>>

    Yes, that thought crossed my mind yesterday, but not knowing in what kind of situations your tool would be used I decided to ignore the "distraction".


    Cindy Meister, VSTO/Word MVP, my blog

    Friday, October 5, 2012 9:31 AM
    Moderator

All replies

  • Which version of Word is involved?

    Cindy Meister, VSTO/Word MVP, my blog

    Wednesday, October 3, 2012 3:28 PM
    Moderator
  • sorry cindy, i forgot to add the specifics

    Word 2010 (& 2007 possibly)

    dotNET 4.0

    Wednesday, October 3, 2012 4:09 PM
  • Hi Matt

    First off, there's not much you can do about avoiding conflicts with other add-ins. There's nothing in the design of Office add-ins (and never has been) that will let one of them get priority, or set the order in which things execute.

    I can think of a couple of reasons why you might remove KeyBindings from a document... A question might be, however, whether this is the best approach? Can you give us more information about the scenario (why, what happens with the saved document, as well as the version that's still being edited, etc.)?

    The best way to get control of what happens during Save/Save As in 2007/2010 is to re-purpose the building command using RibbonXML. That won't avoid any conflict situations, but would give you control over what happens after you issue the Document.Save command. After your code saves the document it can then do other things. But this doesn't account for other add-ins, and cannot account for other add-ins.


    Cindy Meister, VSTO/Word MVP, my blog

    Thursday, October 4, 2012 10:06 AM
    Moderator
  • thanks cindy, further details below

    keybindings are attached to any document that belongs to a project's suite of documents and templates, that is opened in word when the project addin is loaded in the application.

    the keybindings that are attached are applied to the delete and backspace keys, and they bind both keys to a function contained within the project addin. 

    the function provides a method for checking the active selection for the existance of either a page break or a section break and notifies the user if one is found.  it provides the user with a means to cancel the delete and therefore not inadvertantly delete a page or section break from the document.  NOTE: This obviously is not fool proof as they could simply overtype the selection with any other keystroke, thereby bypassing the checking function.

    the need to remove the keybindings before the document is saved is to prevent the delete and backspace keys from failing to function correctly if the document is transefered to another computer where the project addin is not installed.  This failure would happen since the keys would not be able to locate the function that they are bound to, and hence nothing would happen when the keys were pressed, rendering both keys useless.

    The need to reattach the keybindings after the save took place was to ensure that the section checking function was still operable for the open document.

    This whole procedure needs to take account of the SaveAs dialog along with the Save command, and also not execute at all if the save is as a result of the inbuilt autosave set to on in Word's options (as this raised an error).

    The procedure cannot be executed in the DocumentBeforeClose event handler as the user could cancel the save and simply close the document with the keybindings still attached.

    So the DocumentBeforeSave eventhandler performed the following...
    check if the document was part of the project
    remove the keybindings
    execute the Save/SaveAs command itself
    reapply the keybindings
    set the documents Saved property to True
    cancel the Save event to prevent the Save executing again.  (It is this cancellation that i feel is the problem, and maybe i need some extra logic here in order to not have to cancel the Save (but i am yet to produce this).)

    In order to detect if the user had instantiated the save (and not Word internally via the autosave), i interrupt the FileSave, FileSaveAs commands and set a flag to indicate that it is a user initiated call, and not internal to the application.  If it is internal then the eventhandler passes through without doing anything to prevent the error.

    It is interesting to note your details about being unable to prevent conflicts, as recently i had to remove the whole procedure from a client project due to the fact that another 3rd party addin that was installed was requiring the client's documents to be saved into a document management system.  this was also requiring  control of the Save event, and certainly didn't like the bad practice i employed by cancelling the Save event.

    This is where the whole issue of bad practises comes in, as it appears that handling events and beinging able to cancel them could cause conflicts with other addins, in a very random fashion (which addin exectues first).  and certainly interrupting Word's own commands, such as FileSave or FileSaveAs, would do much of the same.  so when developing addins for installation to third party's would you agree that certain practises should not be used at all?  Is there any good documentation on this that you know of?

    but, back to my problem, can you see anywhere where my logic is faulty, or any other possibilities to try?  i could add keybindings in the DocumentChange event, but this would not necessarily fire immediately.  hence me thinking i need a DocumentAfterSave event to handle.

    this function was originally written in VBA and i am currently converting it to c# dotNET.  however, in light of the above, i don't want to be transfering shoddy code, that fails to play nicely, to my glorious new class library!   if you would like me to post the code then please let me know.

    thanks
    m

     
    Thursday, October 4, 2012 1:58 PM
  • Hi Matt

    Thank you for taking the time to provide so much detail :-)

    <<this function was originally written in VBA and i am currently converting it to c# dot>>

    You are aware that you can't use KeyBindings with non-VBA code? So you'd need to at least keep the macros that run for the delete and backspace keys in VBA. Or you'd need to work with Windows API keyboard hooks to get away from VBA completely. I figured it best to mention this, first...

    <<the need to remove the keybindings before the document is saved is to prevent the delete and backspace keys from failing to function correctly if the document is transefered to another computer where the project addin is not installed.>>

    Mmm. I've got an idea and I'm going to do some "thinking out loud" - but I'm not sure of the logic until it all comes out, so bear with me :-)

    Theoretically, you could provide the user with a "link up the keybindings again" button for when the keybindings would get removed, but not reattached (DocumentClose being cancelled). "Undo" can save an indavertant deletion of a break. The two could be combined in an "emergency" button. Then only the SaveAs and Close events are critical; Save, not really.

    Conflicts, however, remain an issue. So I ask myself whether it could make sense to take the "remove keybindings" out of the add-in. Instead, have your add-in monitor DocumentOpen and NewDocument, which can't be cancelled. Maintain a list of document paths and names that have been opened. Monitor SaveAs to get "new" file names". Monitor DocumentBeforeClose to track when any of these may get closed.

    It's easy enough to determine whether there's a file lock on a document, which will tell you whether it was actually closed. And to monitor the Application.Documents collection to check whether a file is still in the list.

    So, at latest when the add-in unloads you should know which documents had been opened and need to have the keybindings removed. The add-in could then trigger a tool that works with these files using the Open XML file format - meaning the work isn't done within the Word application, but on the "closed" files. This can loop through the list it's been keeping of opened documents and remove the keybindings. It should be fairly fast as there's no interaction with the Word application.

    The only "gotcha" I can see here is if, between the time the document is closed and the add-in unloaded, the document is sent to / accessed by someone who doesn't have the add-in?

    The other thought that comes to mind is to make sure the keybindings are in the template attached to the document. That would mean that NO keybindings are linked to the document, itself.


    Cindy Meister, VSTO/Word MVP, my blog

    Thursday, October 4, 2012 2:35 PM
    Moderator
  • thanks cindy, plenty to think about, try and test here.

    firstly, no i didn't know about the keybindings & non vba code, so thanks for that tidbit.  that's saved me at least a week of trawling t'internet to find out why my fancy c# KeyBindingsAdder class didn't work  ;-)
    not sure i want to get stuck into keyboard hooks just yet.

    secondly, i cant provide the relink button as documents are passed to users who definitely wont have any custom functionality installed, and so wouldn't have this tool either.

    i'm liking the sound of the monitor which could remove the bindings from the document after it has closed.  although having not gone anywhere near the Open XML format yet, i'd need to get very dirty in order to achieve this!  this is defo one for a little later

    the potential problem i see here is, what if the documents are saved in a location that my fancy KeyBindingsCleanerMonitor class cannot locate (such as in a CMS), doesn't have the security permissions to access, or the files are write locked?

    your last suggestion mite well be the winner here... as i haven't tried that.  and i think that's by far the easiest to test and will get back to you with results.

    many thanks for your suggestions.  i will keep you posted.

    ultimately, it would appear that i havent programmed this functionality too well in the first place, and whether that's because i'm a bad programmer or the object model has enabled me to adopt bad practices, i'm not sure.  what would you think?

    cheers
    m
    Thursday, October 4, 2012 4:09 PM
  • Hi Matt

    <<secondly, i cant provide the relink button as documents are passed to users who definitely wont have any custom functionality installed, and so wouldn't have this tool either.>>

    You misunderstood my intention :-) The relink functionality would be for the user who may have done something (cancelled close) while working with your tool that removed the keybindings. Just a simple way to re-instate them. IOW the liklihood is higher that the user working with the tool might lose the keybindings and anyone opening the document after that would be less likely to have keybindings.

    <<your last suggestion mite well be the winner here... as i haven't tried that.  and i think that's by far the easiest to test>>

    Yes, absolutely, if it works for you :-)

    <<ultimately, it would appear that i havent programmed this functionality too well in the first place, and whether that's because i'm a bad programmer or the object model has enabled me to adopt bad practices, i'm not sure.  what would you think?>>

    That you're running into design limitations dicated by the way the Office applications were conceived, more than twenty years ago :-) Back then, the idea was to make life easier for the user. But the possibilities provided were so powerful, that they lend themselves to much more - and scenarios that weren't considered when the APIs were designed. I've no idea whether you're a "good" or "bad" programmer - but almost everyone hits these issues when "file management" situations come up.

    <<although having not gone anywhere near the Open XML format yet, i'd need to get very dirty in order to achieve this!>>

    Actually, it probably wouldn't be all that complex. The Keybindings are stored in their own "part" so it would simply be a matter of deleting that, and the entry for its Relationship. Same approach as removing VBA from a document, just a different Part.

    <<the potential problem i see here is, what if the documents are saved in a location that my fancy KeyBindingsCleanerMonitor class cannot locate>>

    Yes, that thought crossed my mind yesterday, but not knowing in what kind of situations your tool would be used I decided to ignore the "distraction".


    Cindy Meister, VSTO/Word MVP, my blog

    Friday, October 5, 2012 9:31 AM
    Moderator
  • hi cindy

    finally had time to test your last suggestion above by placing the keybindings in the template and not in the actual documents.  it only bloomin works a treat, so many thanks for that suggestion!  

    any documents attached to the templates have the keybindings active so that pressing the delete or backspace keys causes the checking function to execute.  but, most importantly, the bindings themselves aren't contained in the document, which means they do not need to be removed before the document is saved!  this therefore negates the previous requirement of handling the BeforeSave event altogether, and also means that i no longer require to override the inbuilt FileSave, FileSaveAs etc functions.

    i tested this by moving the test documents to a location where the templates or global functionality was not installed and the delete and backspace keys performed normallly.  i also tested by simply removing the global functionality and the delete and backspace keys failed to function at all... which is behaviour i fully expected, do not have to be concerned about, and could possibly provide a fix for in any case.

    thanks also for the other suggestions.... and especially for the heads-up on keybindings not working in non-vba code.  that's saved me plenty of potential time wasted.

    cheers

    m

    Wednesday, October 10, 2012 11:30 AM