Previewing a PDF in C# - c#

I would like to create a PDF document with iTextSharp and preview it directly in the application.
And this not only once, but as often as I like, during runtime, when the user makes changes to the text input.
So far it works, but as I said before only once, when the program is started.
When I try to generate the PDF file again, I get an error message,
that the process cannot access the saved PDF document because it is currently being used by another process.
I have already tried to prevent access, but without success so far.
private void CreateDocument()
{
//my attempt to stop the browser from blocking the file acces
if (browser.IsBusy())
{
browser.Stop();
}
doc = new Document(PageSize.A4);
writer = PdfWriter.GetInstance(doc, new FileStream("document.pdf", FileMode.Create));
doc.Open();
cb = writer.DirectContent;
//here is the actual pdf generation
doc.Close();
//this is the part where I set the pdf document reference from the web browser
browser.Navigate(#"path\document.pdf");
}
The actually error occurs where I set the PDFwriter instance.
I've found a page preview component in the toolbox from iTextSharp, but sadly no reference on how to use it. Using that might work easier than trying it with the web browser.

If you don't mind a little bit of flickering just navigate to "about:blank" before you try to save.
If you have a probelm with that, just make a temporary copy of the file and open the copy with the browser. Probably not the best solutions, but should work

My problem was, that the web browser navigation is asynchronous.
As a workaround I used an event listener that keeps track, when the browser actually loaded the document.
For more information about that topic check this question out: https://stackoverflow.com/a/583909/12178103
Down here you can see my complete code
//gets called when the application starts
public Form1()
{
InitializeComponent();
//first time the web browser load operation gets called - make sure to set the event handler
webBrowser1.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(WebBrowserUnload);
WebBrowserLoad();
}
//this button regenerates the pdf
private void Button_Click(object sender, EventArgs e)
{
WebBrowserLoad();
}
//creates the actually pdf document
private void WebBrowserLoad()
{
browser.Hide();
browser.Navigate("about:blank");
}
private void WebBrowserUnload(object sender, WebBrowserDocumentCompletedEventArgs e)
{
if (e.Url.ToString() == "about:blank")
{
doc = new Document(PageSize.A4);
using (fileStream = new FileStream("document\pdf", FileMode.Create))
{
using (writer = PdfWriter.GetInstance(doc, fileStream))
{
PageEventHelper pageEventHelper = new PageEventHelper();
writer.PageEvent = pageEventHelper;
doc.Open();
cb = writer.DirectContent;
//create the pdf here
writer.Flush();
doc.Close();
doc.Dispose();
}
}
browser.Navigate(#"path\document.pdf");
}
else if (e.Url.ToString() == "file:///path/document.pdf")
{
browser.Show();
}
}

Related

How to prevent SaveAs dialog in Word in C#?

I am developing a custom ribbon in Microsoft Word document. I intend to override the save functionality by disabling it and save the document programmatically.
I have added DocumentBeforeSave event Handler to save the document. Here is the part of the code to save the document
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
Globals.ThisAddIn.Application.DocumentBeforeSave += new Word.ApplicationEvents4_DocumentBeforeSaveEventHandler(this.Application_DocumentBeforeSave);
}
public void Application_DocumentBeforeSave(Word.Document document, ref bool saveAsUI, ref bool cancel)
{
String destFolder = #"D:\report\tempFolder\";
Random rnd = new Random();
String fileName = "Temp_" + rnd.Next(1000, 9999).ToString() + ".docx";
var destFile = System.IO.Path.Combine(destFolder, fileName);
document.SaveAs2(destFile);
}
Any idea how to do that?
Be aware, you are trying to save the document by calling the SaveAs2 method in the DocumentBeforeSave event handler. Calling the SaveAs2 method triggers the DocumentBeforeSave method, so you will get a recursion and your dialog will never be displayed.
How to prevent SaveAs dialog in Word in C#?
In the DocumentBeforeSave event handler you may set up the saveAsUI parameter which is true if the Save As dialog box is displayed, whether to save a new document, in response to the Save command; or in response to the Save As command; or in response to the SaveAs or SaveAs2 method.

Copying template doc to active doc

I am making Office plugin to Word. Using Microsoft.Office.Interop. I have few .dotm files(Templates). When pressing a button I need to copy all text from one .dotm to my Active document. I cant figure out how can I put in variable the active document. So I can insert the info.
I tried several ways. Now I am trying to open .dotm copy text from there and then paste it to my active one. But it doesn't work. The Word will start with no errors(while starting in Visual Studio) and then when I am pressing the button it tells me that there is no open document on this: var MyDoc = app.ActiveDocument;
1)
private void Button1_Click(object sender, RibbonControlEventArgs e)
{
var app = new Microsoft.Office.Interop.Word.Application();
var MyDoc = app.ActiveDocument;
var sourceDoc = app.Documents.Open(#"C:\install\CSharp\test.docx");
sourceDoc.ActiveWindow.Selection.WholeStory();
sourceDoc.ActiveWindow.Selection.Copy();
MyDoc.ActiveWindow.Selection.Paste();
2)
var newDocument = new Microsoft.Office.Interop.Word.Document();
newDocument.ActiveWindow.Selection.Paste();
newDocument.SaveAs(#"C:\install\CSharp\test1.docx");
But if I do that way(2):
It will work. But I need to paste into my active document. Also I think that the copy paste method is not so good. Maby there is some other method to import one document into an other.
Never, ever, ever do (2). Using new Microsoft.Office.Interop.Word.Document(); is certain to cause unexpected memory leak problems. Word lets you do it, but it's not supported. new should only ever be used for a new instance of the Word application.
Instead, use Documents.Add referencing the template file. That will create a copy of the template as a new document - very simple:
private void Button1_Click(object sender, RibbonControlEventArgs e)
{
var app = new Microsoft.Office.Interop.Word.Application();
var MyDoc = app.Documents.Add(#"C:\install\CSharp\test.docx");
}

How to loop HTML printing when using a list?

I have a list, which contains paths to html files on my PC. I would like to loop through this list and print them all, in the same order they are in the list.
I tried to loop the code that i have found on msdn.microsoft.com for printing an HTML file.
List<string> AllHTMLsToPrint = new List<string>();
//things added to AllHTMLsToPrint list
foreach (string strHTMLToPrint in AllHTMLsToPrint)
{
PrintHelpPage(strHTMLToPrint);
}
private void PrintHelpPage(string strHTMLToPrint)
{
// Create a WebBrowser instance.
WebBrowser webBrowserForPrinting = new WebBrowser();
// Add an event handler that prints the document after it loads.
webBrowserForPrinting.DocumentCompleted +=
new WebBrowserDocumentCompletedEventHandler(PrintDocument);
// Set the Url property to load the document.
webBrowserForPrinting.Url = new Uri(strHTMLToPrint);
Thread.Sleep(100);
}
private void PrintDocument(object sender, WebBrowserDocumentCompletedEventArgs e)
{
// Print the document now that it is fully loaded.
((WebBrowser)sender).Print();
// Dispose the WebBrowser now that the task is complete.
((WebBrowser)sender).Dispose();
}
You have a design problem here. You walk your list of html pages to print. Then you open the page in a browser. When the page is loaded you print it.
BUT...
Loading the page may take longer than 100ms. This is the time after which the browser loads the next page. You should change your code so that the next page will load after the current one has been printed. You may not want to use a loop in this case but an index which you may want to increment after printing.
Should look similar to this (not tested):
List<string> AllHTMLsToPrint = new List<string>();
private int index = 0;
PrintHelpPage(AllHTMLsToPrint[index]);
private void PrintHelpPage(string strHTMLToPrint)
{
// Create a WebBrowser instance.
WebBrowser webBrowserForPrinting = new WebBrowser();
// Add an event handler that prints the document after it loads.
webBrowserForPrinting.DocumentCompleted +=
new WebBrowserDocumentCompletedEventHandler(PrintDocument);
// Set the Url property to load the document.
webBrowserForPrinting.Url = new Uri(strHTMLToPrint);
}
private void PrintDocument(object sender, WebBrowserDocumentCompletedEventArgs e)
{
// Print the document now that it is fully loaded.
((WebBrowser)sender).Print();
if (index < AllHTMLsToPrint.Count -1)
PrintHelpPage(AllHTMLsToPrint[++index]);
}
You've stated that you have a bunch of local html files.
The loading of local html files may not work by setting the URI.
You could try setting the DocumentStream instead. strHTMLToPrint must then contain the full/relative path to your local html file.
webBrowserForPrinting.DocumentStream = File.OpenRead(strHTMLToPrint);
Not sure what the exact issue is, but I would put this into a background worker so you don't hold up the main thread. I'd also move the loop into the document loaded system, that way as soon as it has loaded and printed it will move onto the next.
That said you haven't said what your code isn't doing.
public partial class Form1 : Form
{
internal List<string> AllHTMLsToPrint = new List<string>();
public Form1()
{
InitializeComponent();
}
public void StartPrinting()
{
//things added to AllHTMLsToPrint list, please note you may need to add file:/// to the URI list if it is a local file, unless it is compact framework
// start printing the first item
BackgroundWorker bgw = new BackgroundWorker();
bgw.DoWork += bgw_DoWork;
bgw.RunWorkerAsync();
/*foreach (string strHTMLToPrint in AllHTMLsToPrint)
{
PrintHelpPage(strHTMLToPrint);
}*/
}
void bgw_DoWork(object sender, DoWorkEventArgs e)
{
PrintHelpPage(AllHTMLsToPrint[0], (BackgroundWorker)sender);
}
private void PrintHelpPage(string strHTMLToPrint, BackgroundWorker bgw)
{
// Create a WebBrowser instance.
WebBrowser webBrowserForPrinting = new WebBrowser();
// Add an event handler that prints the document after it loads.
webBrowserForPrinting.DocumentCompleted += (s, ev) => {
webBrowserForPrinting.Print();
webBrowserForPrinting.Dispose();
// you can add progress reporting here
// remove the first element and see if we have to do it all again
AllHTMLsToPrint.RemoveAt(0);
if (AllHTMLsToPrint.Count > 0)
PrintHelpPage(AllHTMLsToPrint[0], bgw);
};
// Set the Url property to load the document.
webBrowserForPrinting.Url = new Uri(strHTMLToPrint);
}
}

Doubleclick on saved file open my windows form but don't pick up any saved data

I created simple program for save/open practice. Made a setup and associated my program with my own datatype, called it .xxx (for practice).
I managed to Save and Open code and data from textbox but only from my program. Double click (or enter from windows-desktop) open up my WindowsForm as it is but there is an empty textbox. I want my saved file to be opened on double click in the same condition as when I open it from my program. How to set that up??
Here is the code of simple app (cant post images but it simple - got 1 label and 1 textbox with open and save buttons):
private void ButOpen_Click(object sender, EventArgs e)
{
textBox1.Text = "";
DialogResult result = openFileDialog1.ShowDialog();
if (result == DialogResult.OK)
{
string data = Read(openFileDialog1.FileName);
textBox1.Text = data;
}
else
{//do nothing }
}
private string Read(string file)
{
StreamReader reader = new StreamReader(file);
string data = reader.ReadToEnd();
reader.Close();
return data;
}
private void ButSave_Click(object sender, EventArgs e)
{
SaveFileDialog saveFileDialog1 = new SaveFileDialog();
saveFileDialog1.Filter = "Something|*.xxx";
DialogResult result = saveFileDialog1.ShowDialog();
string file = saveFileDialog1.FileName.ToString();
string data = textBox1.Text;
Save(file, data);
}
private void Save(string file, string data)
{
StreamWriter writer = new StreamWriter(file);
writer.Write(data);
writer.Close();
}
NOTE:
My similar question was marked as duplicate but it is not, and this question which was referenced as duplicate Opening a text file is passed as a command line parameter does not help me.It's not the same thing...
Just wanted to find out how to configure registry so windows understand and load data inside the file, or to file save data somehow so i can open it with double click.
So someone please help. If something is not clear I will give detailed information just ask on what point.
Thanks
MSDN has some information about this:
https://msdn.microsoft.com/en-us/library/bb166549.aspx
Basically you need to create an entry in the registry so that explorer.exe knows to launch your program when that file is activated (e.g. double-clicked).
Explorer will then pass the path to the file as an argument to your program.

Closing Stream Read and Stream Writer when the form exits

Hey guys, having a bit of trouble here.
So I have a load button that loads the file, and a save button the saves the file. I also have a exit button that closes the program. What I need help with is when I close the program, I wont to check whether there are any StreamReader or StreamWriter things that have not been closed.
Heres what I have so far:
At the top, i declear these guys
bool isDirtyBoolean = false;
string moreData = "";
My load button looks like this
private void loadToolStripMenuItem_Click(object sender, EventArgs e)
{
//begin in the project folder
openFileDialog1.InitialDirectory = Directory.GetCurrentDirectory();
//display the file open dialog box
DialogResult responseDialogResult;
responseDialogResult = openFileDialog1.ShowDialog();
if (responseDialogResult != DialogResult.Cancel)
{ //check that user did not click the cancel button
//create a streamreader object for the selected file,
//read the file contents,
//and display each line of the file in the list box
StreamReader nameStreamReader = new StreamReader(openFileDialog1.FileName);
while (nameStreamReader.Peek() != -1)
{
freindsDataListBox.Items.Add(nameStreamReader.ReadLine());
}
nameStreamReader.Close();
isDirtyBoolean = true;
}
}
And my save button looks like this
private void saveToolStripMenuItem_Click(object sender, EventArgs e)
{
//begin in the project folder
saveFileDialog1.InitialDirectory = Directory.GetCurrentDirectory();
//display the file save dialog
DialogResult responseDialogResult;
responseDialogResult = saveFileDialog1.ShowDialog();
if (responseDialogResult != DialogResult.Cancel)
{ //check that user did not click the cancel button
//create a streamWriter object and
//then write out the list box items to the file
StreamWriter nameStreamWriter = new StreamWriter(saveFileDialog1.FileName);
int count = freindsDataListBox.Items.Count;
for (int i = 0; i < count; i++)
{
nameStreamWriter.WriteLine(freindsDataListBox.Items[i]);
}
nameStreamWriter.Close();
isDirtyBoolean = true;
freindsDataListBox.Items.Clear();
}
}
My exit button looks like this
private void exitToolStripMenuItem_Click(object sender, EventArgs e)
{
if (isDirtyBoolean == false)
nameStreamReader.Close();
nameStreamWriter.Close();
this.Close();
}
What I tried to do was set up the bool isDirtyBoolean up top, then when the Stream Reader or Writer closes, sets the bool value to true, so when i exit the app, if its still set to false, it closes them anyway.
But this doesnt work because the isDirtyBoolean value is those private void buttons and I cant get to them.
To keep better track of things in general, close your readers and writers in the same method you open them in, rather than letting them live for the duration of the application.
If you use using they will be disposed of (and closed) automatically. Examples:
Reader:
using (StreamReader reader = new StreamReader(openFileDialog1.FileName)) {
// do your stuff...
} // automatic close.
You can do the same with your writer instance.
Or if you're reading and writing at the same time you can open both and auto-close both at the end like so:
using (StreamReader reader = new StreamReader(openFileDialog1.FileName)) {
using (StreamWriter writer = new StreamWriter(path_to_other_file)) {
// now you're reading and writing
// DO YOUR STUFF
} // closes writer.
} // closes reader.
The reason using works as it does is because of the IDisposable interface which is implemented by both the stream reader and writer classes; in fact any class implementing this interface is a candidate for using the using statement on.
And remember to keep an eye out for caveats in the MSDN documentation like so:
StreamWriter.Dispose always closes stream
StreamWriter.Dispose always closes
stream Disposing a StreamWriter closes
the underlying stream when it is
disposed, even if you use a
constructor where you explicitly
provide the stream. Sometimes you want
to keep the stream open, but this
class doesn't currently provide a
mechanism to do that, except to not
call Dispose, but be sure to call
Flush instead.
First of all, you are closing your streams alright - although I would use "using" statement to make sure they are closed even if there is an exception. So no need to close them when form exits.
Also this is bad coding:
if (isDirtyBoolean == false)
nameStreamReader.Close();
nameStreamWriter.Close();
Your if condition only works on the first statement. You need to put curly braces after if.
Use "using" or write your own try/catch/finally blocks and close your streams in the finally.
I'm assuming this is homework so if your assignment requires you to check the streams you are going to have to declare your streams outside of the methods they are currently declared in. When you declare something in a method it only has scope in that method so the code that is currently in your exit button handler shouldn't even compile.
your missing brackets on your if in your exit button handler.

Categories