I am trying to trigger and event after the WokbookDeactivate event is finished because I need to write some metadata into the file and it can be done only when the Excel file is completely closed.
While using WokbookDeactivate the workbook still appears as active and if I try to run the code that access the excel file it raises an exception "is being used by another process..."
ThisAddin_Startup
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
this.Application.WorkbookOpen += new Excel.AppEvents_WorkbookOpenEventHandler(Application_WorkbookOpen);
this.Application.WorkbookDeactivate += Application_WorkbookDeactivate;
}
EventHandler
void Application_WorkbookDeactivate(Microsoft.Office.Interop.Excel.Workbook Wb)
{
Excel.Application excelApp = this.Application;
string filePath = excelApp.ActiveWorkbook.FullName;
string userName = GeneralFunctions.getUser();
try
{
if (File.Exists(filePath) && Metadata.ContainsKey(filePath) && Metadata[filePath])
{
Metadata.Remove(filePath);
}
}
catch (Exception)
{
throw;
}
finally
{
}
}
After Deactivate I need to run another event that check if the key of the excel file was removed from the Metadata. That means I can edit the document using the OpenXML function...
I tried to attach two events to the Deactivate, but the excel workbook is still open when the first one finishes.
Any idea?
Another approach:
I created a Timer property System.Timers.Timer _tm; and call a method to initialize it.
void Application_WorkbookDeactivate(Excel.Workbook Wb)
{
Excel.Application excelApp = this.Application;
string filePath = excelApp.ActiveWorkbook.FullName;
try
{
if (File.Exists(filePath) && Metadata.ContainsKey(filePath) && Metadata[filePath] && Wb.FullName.Equals(filePath))
{
StartWrite(filePath);
}
}
catch (Exception)
{
throw;
}
}
private void StartWrite(string filePath)
{
_tm = new System.Timers.Timer(2000);
_tm.Elapsed += (sender, args) => _tm_Elapsed(sender, filePath);
_tm.Enabled = true;
}
void _tm_Elapsed(object sender, string filePath)
{
try
{
((System.Timers.Timer)sender).Enabled = false;
string userName = GeneralFunctions.getUser();
if (this.Application != null && Metadata[filePath])
{
// Do stuff with the file
_tm.Stop();
}
}
catch (Exception)
{
throw;
}
}
Related
I have a Xamarin.Forms application that supports only UWP. I cannot find a way to print a pdf document. Whatever I have seen on the web, for some reason doesn't work for me. E.g. I tried
https://www.syncfusion.com/kb/8767/how-to-print-pdf-documents-in-xamarin-forms-platform
It lets me print, but the preview in the print dialog never shows up, and the progress indicator just keeps rotating forever.
I also tried http://zawayasoft.com/2018/03/13/uwp-print-pdf-files-silently-without-print-dialog/
This gives me errors that I cannot fix.
So I wonder if somebody can suggest something else that would actually work. Maybe something newer than what I have tried (I use VS 2017). Printing without the printing dialog would be preferable.
Thank you in advance.
I used a very dirty hack to do that!
What I had to do was to try to print the image version of the pdf (I did the conversion in backend) and then used the following DependencyInjection:
Inside my Print class in UWP project:
class Print : IPrint
{
void IPrint.Print(byte[] content)
{
Print_UWP printing = new Print_UWP();
printing.PrintUWpAsync(content);
}
}
and the class responsible for printing in uwp:
public class Print_UWP
{
PrintManager printmgr = PrintManager.GetForCurrentView();
PrintDocument PrintDoc = null;
PrintDocument printDoc;
PrintTask Task = null;
Windows.UI.Xaml.Controls.Image ViewToPrint = new Windows.UI.Xaml.Controls.Image();
public Print_UWP()
{
printmgr.PrintTaskRequested += Printmgr_PrintTaskRequested;
}
public async void PrintUWpAsync(byte[] imageData)
{
int i = 0;
while (i < 5)
{
try
{
BitmapImage biSource = new BitmapImage();
using (InMemoryRandomAccessStream stream = new InMemoryRandomAccessStream())
{
await stream.WriteAsync(imageData.AsBuffer());
stream.Seek(0);
await biSource.SetSourceAsync(stream);
}
ViewToPrint.Source = biSource;
if (PrintDoc != null)
{
printDoc.GetPreviewPage -= PrintDoc_GetPreviewPage;
printDoc.Paginate -= PrintDoc_Paginate;
printDoc.AddPages -= PrintDoc_AddPages;
}
this.printDoc = new PrintDocument();
try
{
printDoc.GetPreviewPage += PrintDoc_GetPreviewPage;
printDoc.Paginate += PrintDoc_Paginate;
printDoc.AddPages += PrintDoc_AddPages;
bool showprint = await PrintManager.ShowPrintUIAsync();
}
catch (Exception e)
{
Debug.WriteLine(e.ToString());
}
// printmgr = null;
// printDoc = null;
// Task = null;
PrintDoc = null;
GC.Collect();
printmgr.PrintTaskRequested -= Printmgr_PrintTaskRequested;
break;
}
catch (Exception e)
{
i++;
}
}
}
private void Printmgr_PrintTaskRequested(PrintManager sender, PrintTaskRequestedEventArgs args)
{
var deff = args.Request.GetDeferral();
Task = args.Request.CreatePrintTask("Invoice", OnPrintTaskSourceRequested);
deff.Complete();
}
async void OnPrintTaskSourceRequested(PrintTaskSourceRequestedArgs args)
{
var def = args.GetDeferral();
await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
args.SetSource(printDoc.DocumentSource);
});
def.Complete();
}
private void PrintDoc_AddPages(object sender, AddPagesEventArgs e)
{
printDoc.AddPage(ViewToPrint);
printDoc.AddPagesComplete();
}
private void PrintDoc_Paginate(object sender, PaginateEventArgs e)
{
PrintTaskOptions opt = Task.Options;
printDoc.SetPreviewPageCount(1, PreviewPageCountType.Final);
}
private void PrintDoc_GetPreviewPage(object sender, GetPreviewPageEventArgs e)
{
printDoc.SetPreviewPage(e.PageNumber, ViewToPrint);
}
}
Please note that this is not a perfect solution and sometimes it crashes without actually being able to trace the exception (which is really strange) so I am sure there must be better answers even though it does the job.
I am having a problem with a with a code that I used for my C# application. When I click on the browse button and select the file dialogue box opens twice.
private void txt_location_TextChanged(object sender, EventArgs e)
{
string folderPath = "";
FolderBrowserDialog folderBrowserDialog1 = new FolderBrowserDialog();
if (folderBrowserDialog1.ShowDialog() == DialogResult.OK) {
folderPath = folderBrowserDialog1.SelectedPath;
}
}
private void Button21_Click(object sender, EventArgs e)
{
using(var fbd = new FolderBrowserDialog()) {
DialogResult result = fbd.ShowDialog();
if (result == DialogResult.OK && !string.IsNullOrWhiteSpace(fbd.SelectedPath)) {
selectedPath = fbd.SelectedPath;
txt_location.Text = selectedPath;
}
}
}
private void bunifuThinButton21_Click_1(object sender, EventArgs e)
{
System.IO.StreamWriter file;
bool isvalid = ValidateInputs();
try {
file = new System.IO.StreamWriter(selectedPath + # "\dred.txt");
catch (Exception ex) {
MessageBox.Show("Please, Select valid Path..");
return;
}
try {
if (isvalid) {
WriteLines(file);
}
}
catch (Exception ex2) {
MessageBox.Show(ex2.Message);
}
finally {
file.Close();
}
}
}
Obviously, it is only meant to open once to enable me to read the selected file. This works, but only once I have selected the file twice.
Thanks in advance for your help.
You have FolderBrowserDialog being instantiated and shown on two different events:
txt_location_TextChanged
and
Button21_Click
You're having two popups because each one is firing once separately.
You should probably remove the event txt_location_TextChanged entirely unless you need it for another thing that isn't popping the FolderBrowserDialog again.
I created an Office Add-In project and I added ribbon menu for application. When I build my project word document have my ribbon there is no problem.
How can I save the active document as a file using StreamReader when clicking on a button from the ribbon menu using the button click event below?
private void btnsavefile_Click(object sender, RibbonControlEventArgs e)
{
//Getting FileStream here.
}
I found the following solution in Stack Overflow. Hopefully it is relevant to you.
Serialize current ActiveDocument from office 2007 add-in
Personally, I have done the same when I was dealing with this scenario. I have saved a copy of the file to the temporary location and pushed the copy to the server. In this case, the active document stays as is.
Excel.Workbook xlb = Globals.ThisAddIn.Application.ActiveWorkbook;
xlb.SaveCopyAs(filePath);
Hope this helps!
void Application_DocumentBeforeClose(Word.Document document, ref bool Cancel)
{
try
{
string filePath = this.Application.ActiveDocument.FullName.ToString();
string fileName = this.Application.ActiveDocument.Name;
//dialogFilePath = filePath;
dialogFileName = fileName;
string tempFile;
string tempPath;
if (true)
{
var confirmResult = System.Windows.Forms.MessageBox.Show("Are you sure to save this document ??",
"Confirm Save!!",
System.Windows.Forms.MessageBoxButtons.YesNo);
if (confirmResult == System.Windows.Forms.DialogResult.Yes)
{
//document.Save();
var iPersistFile = (IPersistFile)document;
iPersistFile.Save(tempPath, false);
//Do some action here
}
Word._Document wDocument = Application.Documents[fileName] as Word._Document;
//wDocument.Close(Word.WdSaveOptions.wdDoNotSaveChanges);
ThisAddIn.doc.Close(Word.WdSaveOptions.wdDoNotSaveChanges);
}
}
catch (Exception exception)
{
}
}
Create Word Addin project-> Add Ribbon visual designer from add new item.
Add menu to Ribbon designer and write below code in ribbonsample.cs
public partial class RibbonSample
{
private void RibbonSample_Load(object sender, RibbonUIEventArgs e)
{
// Initialise log4net
}
//Adding items in menu from DB
public RibbonSample()
: base(Globals.Factory.GetRibbonFactory())
{
InitializeComponent();
try
{
System.Data.DataTable dt = new DataAcces().GetData();
if (dt.Rows.Count > 0)
{
for (int i = 0; i < dt.Rows.Count; i++)
{
RibbonButton Field = this.Factory.CreateRibbonButton();
Field.Label = dt.Rows[i][1].ToString();
Field.Tag = i;
Field.ControlSize =
Microsoft.Office.Core.RibbonControlSize.RibbonControlSizeLarge;
Field.Click += Field_Click;
menu1.Items.Add(Field);
}
}
else
{
System.Windows.Forms.MessageBox.Show("No Fields are available in database");
}
}
catch (Exception exception)
{
//thrw exception
}
}
//Select menu item text in word
void Field_Click(object sender, RibbonControlEventArgs e)
{
try
{
Microsoft.Office.Interop.Word.Range currentRange = Globals.ThisAddIn.Application.Selection.Range;
currentRange.Text = (sender as RibbonButton).Label;
}
catch (Exception exception)
{
log.Error(friendlyErrorMessage + " Field_Click Details:" + exception.Message, exception);
}
}
}
I tried to download file like this:
WebClient _downloadClient = new WebClient();
_downloadClient.DownloadFileCompleted += DownloadFileCompleted;
_downloadClient.DownloadFileAsync(current.url, _filename);
// ...
And after downloading I need to start another process with download file, I tried to use DownloadFileCompleted event.
void DownloadFileCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
{
if (e.Error != null)
{
throw e.Error;
}
if (!_downloadFileVersion.Any())
{
complited = true;
}
DownloadFile();
}
But, i cannot know name of downloaded file from AsyncCompletedEventArgs , I made my own
public class DownloadCompliteEventArgs: EventArgs
{
private string _fileName;
public string fileName
{
get
{
return _fileName;
}
set
{
_fileName = value;
}
}
public DownloadCompliteEventArgs(string name)
{
fileName = name;
}
}
But I cannot understand how call my event instead DownloadFileCompleted
Sorry if it's nood question
One way is to create a closure.
WebClient _downloadClient = new WebClient();
_downloadClient.DownloadFileCompleted += DownloadFileCompleted(_filename);
_downloadClient.DownloadFileAsync(current.url, _filename);
This means your DownloadFileCompleted needs to return the event handler.
public AsyncCompletedEventHandler DownloadFileCompleted(string filename)
{
Action<object, AsyncCompletedEventArgs> action = (sender, e) =>
{
var _filename = filename;
if (e.Error != null)
{
throw e.Error;
}
if (!_downloadFileVersion.Any())
{
complited = true;
}
DownloadFile();
};
return new AsyncCompletedEventHandler(action);
}
The reason I create the variable called _filename is so that the filename variable passed into the DownloadFileComplete method is captured and stored in the closure. If you didn't do this you wouldn't have access to the filename variable within the closure.
I was playing around DownloadFileCompleted to get the file path/file name From the event. I've tried the above solution also but It was not like my expectation then i fond the solution by adding Querystring value, Here i would like to share the code with you.
string fileIdentifier="value to remember";
WebClient webClient = new WebClient();
webClient.DownloadFileCompleted += new AsyncCompletedEventHandler (DownloadFileCompleted);
webClient.QueryString.Add("file", fileIdentifier); // here you can add values
webClient.DownloadFileAsync(new Uri((string)dyndwnldfile.path), localFilePath);
And the event can be defined as like this:
private void DownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
{
string fileIdentifier= ((System.Net.WebClient)(sender)).QueryString["file"];
// process with fileIdentifier
}
private void button1_Click(object sender, EventArgs e)
{
new System.Threading.Thread(delegate()
{
Export();
}).Start();
}
private void Export()
{
int rowcount = ((System.Data.DataTable)this.dgResult.DataSource).Rows.Count;
System.Data.DataTable dt = (System.Data.DataTable)this.dgResult.DataSource;
if (rowcount > 0)
{
if (InvokeRequired)
{
BeginInvoke(new MethodInvoker(delegate()
{
svDialog.Filter = "Excel|*.xls";
svDialog.Title = "Save an Excel File";
svDialog.ShowDialog();
if (svDialog.FileName != "")
{
Business.ExportToExcel.ExcelFromDataTable(dt, svDialog.FileName);
MessageBox.Show("Export completed");
}
}));
}
else
{
svDialog.Filter = "Excel|*.xls";
svDialog.Title = "Save an Excel File";
svDialog.ShowDialog();
if (svDialog.FileName != "")
{
Business.ExportToExcel.ExcelFromDataTable(dt, svDialog.FileName);
MessageBox.Show("Export completed");
}
}
}
else
{
MessageBox.Show("No data found");
}
}
when the button1 is clicked then export method is getting called in separate thread and no error raise but save file dialog is not getting error. so please tell me what is my mistake in the code. my approach is wrong to call a method in separate thread. also explain plzz save file dialog is not opening. which area i need to rectify. plzz explain. thanks.
Keep in your mind, that all Winforms object should be used from main UI thread. So in separate thread you MUST use Invoke/BeginInvoke. If you can, do all "Winforms stuff" in UI thread and after that run separate thread with all data/informations which is required.
I think, the better way is:
private void button1_Click(object sender, EventArgs e) {
this.Export();
}
private void Export() {
System.Data.DataTable dt = (System.Data.DataTable)this.dgResult.DataSource;
if ( dt.Rows.Count > 0 ) {
// initialize save file dialog
DialogResult rslt = this.svDialog.ShowDialog(this);
if ( rslt == DialogResult.OK ) {
string filePath = this.svDialog.FileName;
// QueueUserWorkItem runs target delegate in separate thread
ThreadPool.QueueUserWorkItem( (_state)=> this.Export(dt, filePath) );
}
}
else {
// ... some other code ....
}
}
private void Export(DataTable data, string filePath) {
Exception thrownException = null;
try { Business.ExportToExcel.ExcelFromDataTable(dt, filePath); }
catch( Exception exc ) { thrownException = exc; }
if ( null == thrownException ) { MsgBox("Export completed."); }
else { MsgBox("Error: " + thrownException.Message); }
}
private void MsgBox(string text) {
if (this.InvokeRequired) {
Action<string> dlg = this.MsgBox;
this.Invoke( dlg, text );
}
else {
MessageBox.Show(this, text);
}
}