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.
Related
First of all I would explain the situation.
I have a GUI which is stored on a server and should gain multiple user access. Users are working on different tabs (kind of separated workspaces).
Now when a user enters a tab it should be locked for other users (disabled).Other users should see if a tab is currently locked.
So my ideas on this are:
Create a textfile which lists the tabs and their current status (locked-unlocked).
A thread checks every few seconds if that file was modified. If yes then read it and disable or enable the corresponding tabs in the GUI.
3.When a user wants to enter a tab, check the file if the tab is unlocked (maybe a user was faster than the thread) and set it to locked.
When a user leaves a tab then set it to unlocked.
Now my questions are:
Is there a better or more efficient way to handle this situation?
And how to avoid the case that two users want to switch to the same tab at the same time, read that the tab is unlocked and write at the same time into the file?
Are there even more error cases?
Instead of a text file, you could use a proper SQL DBMS and transactions to keep the checking and updating atomic. It will cover all edge cases if you write your queries properly.
Something like:
begin tran
if not exists(select top 1 1 from tabs where id=#id and active=1)
begin
update tabs set active=1 where id=#id
select 1 --result
end
else
begin
select 0 --result
end
commit tran
Or even better, instead of just storing true/false to mean that the tab is in use, store a user identifier to know who's using it. That way if your application crashes/ends before releasing it, you can manually release it next time the same user logs in. You can also throw in an expiration date for the same purpose, and keep pushing it back while the application is online.
I understand from your question that you need to synchronize the applications running on different computers. You want to do it through a file located on a network drive, that is accessed from all of these computers, right?
Except shared file you can use IP/Ethernet broadcasting, or simple synchronizing server.
Among all these solutions the shared file is most simple, but no so effective.
Instead of it you can use similar technique with System.IO.FileSystemWatcher. In case of file watcher you'll create single file for each tab while it's locking; and delete it while tab is releasing.
File watcher frees you from having to re-read the content of the file, it raises an event instead when the file is changed.
This method would work with no so high load. If you're planning >50 users I think it's better to develop special server to synchronize users.
Short instructions:
Place FileSystemWatcher control on a form.
Setup property Path to desired network path (it's better to use new empty directory).
Create handlers for event Create and Delete:
void fileSystemWatcher1_Created(object sender, FileSystemEventArgs e)
{
var tabName = Path.GetFileName(e.FullPath);
var tab = tabControl1.TabPages[tabName];
// Do not disable selected tab cause you're working with the tab
// and you're locking it
if (tab != tabControl1.SelectedTab)
tab.Enabled = false;
}
void fileSystemWatcher1_Deleted(object sender, FileSystemEventArgs e)
{
var tabName = Path.GetFileName(e.FullPath);
var tab = tabControl1.TabPages[tabName];
tab.Enabled = true;
}
When a tab gets focus, you should create empty file with the tab name in the network directory:
. . .
var path = fileSystemWatcher1.Path;
var filename = Path.Combine(path, tabControl1.SelectedTag.Name);
using (File.Create(filename));
When a tab losts focus: you should delete file:
var path = fileSystemWatcher1.Path;
var filename = Path.Combine(path, tabControl1.SelectedTag.Name);
File.Delete(filename);
I feel xml file will be better than text file for this. As specified in the question you need to maintain status of each tab in the xml file indicating whether it is locked. you can handle selecting event of the tab control in which the type parameter e is TabControlCancelEventArgs and has the property Cancel by setting which to true will cancel selecting that tab. within this event you can check the xml file to verify that the tab is locked or unlocked and if locked, you can set e.Cancel=true and give a message to the user that this tab is locked.
you can use deselecting event of the tab control to know when a user moves out of that tab and update the status of that tab in xml file to unlocked.
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?
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
Scenario:
Existing winforms application with embedded word using dsoframer
Problem:
When user hits preview and exits preview, the fields are showing the codes like '{FORMTEXT}' instead of the actual values. When I click on print options on print preview, the 'Show field codes instead of their values' option is disabled. Don't know why.
I am looking to achieve previewclosed event so I can iterate through the fields and set the field.ShowCodes to false.
The print options are not relevant to what the user sees when print preview exits. Word has no event that triggers for print preview. But you don't need to iterate through the fields to ensure that field codes are not displaying. Use:
Document.ActiveWindow.View.ShowFieldCodes = False
Is there a way to override the functionality of the preview exit? Then you can send a callback before you exit the preview.
I'm trying to write a program with C# that sends text into other windows.
How do I write a command in C# that sends a text into the window that is currently under the users focus?
For example:
If the user clicks an open notepad window, or an open outlook letter, or an open excel sheet, and then clicks the button on my program, a text will be "pasted" directly into the last notepad window/outlook letter/excel cell that the user clicked on last.
I hope my question is clear enough. I'm not so experienced and am missing a lot of terminology.
Take your application out of focus by minimizing or hiding the main window, and then send your text with
SendKeys.SendWait("Hello World!");
Finally, restore your main window.
If the code is executed in the main form, you could do this
this.Visible = false;
SendKeys.SendWait("Hello World!");
this.Visible = true;
Olivier's response actually seems more accurate (and taught me something :)) than my original "does not seem achievable". If you need an example, then take a look at this:
http://www.codeproject.com/Articles/18366/Sending-Keystrokes-to-another-Application-in-C
However, on a more complex level, without an API to call into, there is not much more that you can do beyond this solution.