In Word VSTO we have DocumentBeforeSave event which is called right after "Want to save your changes" dialog and I can easy cancel standard Save dialog and show my own.
But in Excel VSTO WorkbookBeforeSave is called after Save dialog closed and it causes my own save dialog to be shown after built-in one. I can use WorkbookBeforeClose event but I should show my own "Want to save your changes" dialog and also autosave functionality will not work when pressing "Don't save".
Is there way to call my code right after "Want to save your changes" dialog in Excel with ability to prevent built-in "Save" dialog or somehow tell Excel to create an autosave point (with my own "Want to save your changes" dialog) when I press "Don't save"?
I'm not sure if you found your answer as this thread is a bit dated but thought I'd put in my $0.02.
I create a [isDirty] named range on an unlocked veryhidden sheet and on each visible Worksheet.Change I set the [isDirty]=True
In the Workbook.BeforeClose I have this:
If [IsDirty] = True Then
Select Case MsgBox("Do you want to save the changes you made to '" & .Name & "'?", _
vbYesNoCancel + vbExclamation)
Case Is = vbYes
ThisWorkbook.Save
[IsDirty] = False
Case Is = vbNo
'Do not save
Case Is = vbCancel
Cancel = True
End Select
End If
In Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)
ThisWorkbook.Save
[IsDirty] = False
Cancel=True
Related
I am developing a MS Word Add-In, so I added a button that fires a function :
private void button1_Click(object sender, RibbonControlEventArgs e) {
// do some actions on a word document (text - formatting - ...) here.
}
My issue is, when the funcation performs n actions on the word document, I have to click undo n times to undo all button actions. This is a bad user experience to undo like 10 or 100 times to return to original state (text - formatting - ... etc).
Is there some way to pack all button actions as one action in the undo stack, so that I can undo button effect with a single click or Ctrl + z ?
Important note:
An alternative approach that does the job for me is to:
Open temp document.
Copy original document to temp document.
Do all edits on temp doc ( n actions taken on it).
Copy temp doc back to original doc (only one action taken which is paste, so I can undo it).
This is why I struggled in the second approach: check here
You could wrap everything in a custom undo-record - that way you only have to undo once to undo it all.
Visual Basic:
Application.UndoRecord.StartCustomRecord "Title of undo-record here"
Application.ScreenUpdating = False ' Optional, reduces screen flicker during operations.
'--- Your code here
Application.ScreenUpdating = True ' Optional, but required if set to false above
Application.UndoRecord.EndCustomRecord
' After this, you can undo once to undo it all.
C#:
Application.UndoRecord.StartCustomRecord "Title of undo-record here";
Application.ScreenUpdating = false; // Optional, reduces screen flicker during operations.
//--- Your code here
Application.ScreenUpdating = true; // Optional, but required if set to false above
Application.UndoRecord.EndCustomRecord;
// After this, you can undo once to undo it all.
This StackOverFlow Answer should point you in the right direction.
Save undo stack during macro run
I'm creating an application to automate some processes. One of them is creating docx file from the dotx template.
Steps are quite easy: app opens MS Word with test.dotx file and SaveAs it to c:\temp as a test.docx. It should be as close to user's actions as possible. When the file is opened (from dotx so it is docx already) all I need is to open SaveAs dialog and push "save" (or just "enter", because focus is set on "save" button).
The problem is how to "hit" the save/enter. I tried SendKeys but I am in ShowDialog() which is waiting for the result and cannot perform SendKeys at the moment. Of course if I press enter from keyboard or cklick on "Save" all works perfectly, but this one "press" I'd like to do from the code. Could you please point me how to solve this (if it is possible at all)? Thank you.
Here is the part of the code I'm strugglig with:
SaveFileDialog saveFileDialog1 = new SaveFileDialog
{
InitialDirectory = #"C:\Temp\",
DefaultExt = "docx",
FileName = "test"
};
if (saveFileDialog1.ShowDialog() == DialogResult.OK)
{
object FileName = saveFileDialog1.FileName;
doc.SaveAs(ref FileName);
}
If hit the save/enter is your requirement, the recommendation is use Spy++ (this Tool can be installed by Visual Studio Installer) to capture both: "the dialog with save button" and "save button" alias/class; then, programming with C# PInvoke (for example, using FindWindow and FindWindowEx) to send/post message to simulate this "save" button.
I know there is already a post with the same name as this but it provides a partial solution for the problem. The post is: VSTO Word post save event
I'm using this class and it helped me a lot. However, when I make changes in the Word file and try to Close the application clicking in the option "Not Save" the event of save is raised.
How can I know if the user has clicked in the "Save" or "Not Save" buttom when trying to close the window? I've tried everything but I can't know this information.
A quick test in VBA makes me believe this approach would be promising. DocumentBeforeClose is triggered before DocumentBeforeSave.
Declare a class-level field (saveStatus in the below code snippet).
In DocumentBeforeClose set it to "False" on the assumption the user won't save. If the user does save, set the value to True in DocumentBeforeSave. If you need to do something with the document when it's saved, put that code in this event, as well.
Private saveStatus as Boolean
Private Sub app_DocumentBeforeClose(ByVal Doc As Document, Cancel As Boolean)
saveStatus = False
Debug.Print saveStatus
End Sub
Private Sub app_DocumentBeforeSave(ByVal Doc As Document, SaveAsUI As Boolean, Cancel As Boolean)
saveStatus = True
Debug.Print saveStatus
End Sub
For those facing the same problem I found a solution. Before I open the file in Word I read all bytes and store in a class variable, for example, wordContent using
wordContent = System.IO.File.ReadAllBytes(path);
Then, every time that I save without closing the application (i.e clicking in the save buttom in Word) I update this variable wordContent.
So, when I close the application and the event AfterSave is fired with my variable isClosed == true, in this point I don't know if the user closed the application clicking in Save or Not Save. So, I read the bytes of the word File and compare with my wordContent. When the user has clicked in the "Save" option the contents will be different, and when the user has clicked in the "Not Save" options the contents will be equal.
So, whatever it needs to be done it'll be in the comparison of this two byte arrays.
Remembering that I'm using the WordSaveHandler provided by the post that I pointed in the question above, this class can handle if the users save by clicking in the Save Buttom or by closing the application.
I have a c# method that needs to create two documents separately using PDF995. The simplified version:
public void PrintDocuments(PrintDocument report1, PrintDocument report2) {
PrintUtility.SetPDF995Key(); // Method to set the product code in the registry.
report1.Print(); // PDF995 "Save as" dialog box appears here.
PrintUtility.SetPDF995Key();
report2.Print();
}
In order to create a document and avoid having all the PDF995 advertising banners appear, I first have to set the Product Code as a Registry item. However as soon as you click on Save in the PDF995 Save File Dialog, the product key is blanked out, which means I would have to set the Product Code again to create the second document.
The problem is, the Save File dialog box is displayed as an asynchronous / modeless dialog box, which means that the code which creates the second document is reached before the user has had a chance to click on Save for the first one.
So now, even though the Product Code is set a second time, clicking on Save for the first document will blank it out, and because the program has already passed the call to the PrintUtility.SetPDF995Key() method before printing the second one, it will still be blank when the second Save File Dialog appears and so I get the annoying "Trial version" banners.
So is there any way around this? e.g. is there a way to make the Save As dialog Modal, or an event when "Save" is clicked that can be fired so I can call PrintUtility.SetPDF995Key() immediately, or some way of permanently setting the Product Code or something else?
I have some functionality inside DocumentBeforeSave event handler.
That's should work only when user manually invoke Save (press Save button).
But word 2007 have autosave function and event DocumentBeforeSave throws each time when autosave work. How to check that save is invoked over Autosave or User manually invoke Save?
It looks like there's no build-in way of doing that because the object model simply doesn't support it (per this link), but you can use VBA to override the default save hotkey and button click, and send those calls to your .NET assembly (per this link). Just make sure you invoke save manually afterwards to make sure the document actually saves.
There are actually multiple ways to tell the difference.
Option 1 (best)
Application.WordBasic.IsAutosaveEvent
Option 2 (what I did before finding option 1)
Intercept the FileSave (and FileSaveAs) command from a ribbon:
<customUI xmlns="http://schemas.microsoft.com/office/2009/07/customui" onLoad="Ribbon_Load">
<commands>
<command idMso="FileSave" onAction="SaveIntercept" />
</commands>
</customUI>
Set a flag in the SaveIntercept method, and set CancelDefault to false so the save continues.
public void SaveIntercept(IRibbonControl control, ref bool CancelDefault)
{
logger.Info("Intercepted Manual Save");
ManualSave = true;
CancelDefault = false;
}
Also implement BeforeDocument_BeforeSave, and check the flag there. If the flag was set, it was manual, otherwise it's an autosave (or potentially comes from another Add-In; not sure if that works).
bool quit = !customizations.ManualSave;
if (quit)
{
logger.Info("Autosave. Allowing Word to handle this save.");
e.Cancel = false;
return;
}
else
{
logger.Info("Manual save. Proceeding.");
customizations.ManualSave = false;
}
This covers saves through the backstage buttons, the quick access toolbar buttons, and the save shortcut (even if they redefine the keyboard shortcut)
Interestingly, there is a way to tell after the save as well, as described here (updated version here).