I'm trying to add a Print function to a WPF Project. The user can add and delete text to textbox or other controls at runtime so the size of the xaml control is not fixed. If he clicks the print button i want to figure out the size of the control and print a (if neccessary multipage) document. this is my code so far
public void Print(FrameworkElement element)
{
System.Windows.Controls.PrintDialog printDlg = new System.Windows.Controls.PrintDialog();
if (printDlg.ShowDialog() == true) {
double height = element.ActualHeight;
double width = element.ActualWidth;
Size pageSize = new Size(printDlg.PrintableAreaWidth, printDlg.PrintableAreaHeight);
//element.Measure(pageSize);
//element.Arrange(new Rect(5, 5, pageSize.Width, pageSize.Height));
printDlg.PrintVisual(element, "this is a test");
}
}
My idea is to check the actual height and width of the control. If one of these is larger than the pagesize, I know i have to print multiple pages. I'm not quite sure how to do that, but i think i will have to make use of XPSDocument class. Can someone please help me, I dont know how I can split my document into multiple pages and print them and can someone also tell me how to create a FlowDocument from my xaml code.
thanks in advance!
By default, you can't print control on multiple pages, but you can do it manually.
Here's the GREAT article, which solves your problem:
http://www.codeproject.com/Articles/164033/WPF-Visual-Print-Component
Related
I have HTML code something like -
<html>
<body>
<table><tr><td><h1>Heading</h1></td></tr></table>
</body>
</html>
In a specific requirement, I need to know in advance that how much height will this HTML will take to display in fullscreen. So that I keep that much space calculated specifically for that content.
What I thought was to render this code in WebBrowser control, and then take the height.
this.webBrowser1.Url = new Uri(htmlFilePath);
//The below code will force the webbrowser control to load the HTML in it.
while (this.webBrowser1.Document.Body == null)
{
Application.DoEvents();
}
int height = this.webBrowser1.Document.Body.ScrollRectangle.Height;
But in console application I can't use WebBrowser control. Is there any other way I can accomplish this?
Answering my own question -
All I needed to do was adding reference of System.Windows.Forms in my console application.
After that I was able to use WebBrowser in headless form directly without creating any form element.
After using WebBrowser, I set the height/width of reasonable number. Something like below -
WebBrowser wb = new WebBrowser();
//Setting the page height and width to a reasonable number. So that whatever the content loaded in it gets aligned properly.
//For me 3000 works fine for my requirements.
wb.Width = 3000;
wb.Height = 3000;
wb.Url = new Uri(htmlFilePath);
//the below code will force the webbrowser control to load the html in it.
while (wb.Document.Body == null)
{
Application.DoEvents();
}
int height = wb.Document.Body.ScrollRectangle.Height;
I know how to show page numbers and how to align them in footer. However my problem is that my Footer contains some custom text which should be left aligned and page number should be aligned to right corner.
string footer = "My custom footer";
Paragraph footerParagraph = section.Footers.Primary.AddParagraph(footer);
footerParagraph.AddTab();
footerParagraph.AddPageField();
Above will generate "My custom footer 1" for page 1, I need page nmuber to be right at the right most corner of the page. I can add extra spaces or tab but thought there must be a clean way to achieve this. Thanks.
Keep it Simple: Use a Tab Stop
The best way to do this is the same as you would do in most word processing tools: with a right-aligned tab-stop, placed on the right margin of the page. This is pretty straight forward, but I couldn't find the "full" solution anywhere, so here's what you need:
// Grab the current section, and other settings
var section = documentWrapper.CurrentSection;
var footer = section.Footers.Primary;
var reportMeta = documentWrapper.AdminReport.ReportMeta;
// Format, then add the report date to the footer
var footerDate = string.Format("{0:MM/dd/yyyy}", reportMeta.ReportDate);
var footerP = footer.AddParagraph(footerDate);
// Add "Page X of Y" on the next tab stop.
footerP.AddTab();
footerP.AddText("Page ");
footerP.AddPageField();
footerP.AddText(" of ");
footerP.AddNumPagesField();
// The tab stop will need to be on the right edge of the page, just inside the margin
// We need to figure out where that is
var tabStopPosition =
documentWrapper.CurrentPageWidth
- section.PageSetup.LeftMargin
- section.PageSetup.RightMargin;
// Clear all existing tab stops, and add our calculated tab stop, on the right
footerP.Format.TabStops.ClearAll();
footerP.Format.TabStops.AddTabStop(tabStopPosition, TabAlignment.Right);
The hardest part of this, is figuring out what your tab stop position should be. Because I'm boring and really like encapsulation, I dynamically calculate the tab stop position, based on the page width, less the horizontal page margins. However, getting the current page width wasn't as easy as I'd thought it'd be, because I'm using PageFormat to set the page dimensions.
Next Challenge: Getting Your Page Width, Dynamically
First, I really hate having tightly coupled code (think: fan-in and fan-out), so even though I know at this point in time what my page width is, even to the point of hard-coding it, I still want to hard code it in only a single place, then refer to that one place everywhere else.
I keep a custom "has-a"/wrapper class to keep this stuff encapsulated into; That's documentWrapper in my code here. Additionally, I don't expose any of the PDFSharp/MigraDoc types to the rest of my application, so I'm using ReportMeta as a way to communicate settings.
Now for some code. When I setup the section, I'm using the MigraDoc PageFormat to define the size of my page for the current section:
// Create, and set the new section
var section = documentWrapper.CurrentDocument.AddSection();
documentWrapper.CurrentSection = section;
// Some basic setup
section.PageSetup.PageFormat = PageFormat.Letter; // Here's my little bit of hard-coding
Unit pageWidth, pageHeight;
PageSetup.GetPageSize(PageFormat.Letter, out pageWidth, out pageHeight);
var reportMeta = documentWrapper.AdminReport.ReportMeta;
if (reportMeta.PageOrientation == AdminReportMeta.ORIENT_LANDSCAPE)
{
section.PageSetup.Orientation = Orientation.Landscape;
documentWrapper.CurrentPageWidth = pageHeight;
}
else
{
section.PageSetup.Orientation = Orientation.Portrait;
documentWrapper.CurrentPageWidth = pageWidth;
}
What's really important here, is that I'm storing the CurrentPageWidth, this becomes really important when setting up our tab stops. The CurrentPageWidth property, is simply a MigraDoc Unit type. I am able to determine what this is by using MigraDoc's PageSetup.GetPageSize with my chosen PageFormat.
A single tab will do. Create a right-aligned tab at the right-most position.
You can set the tab stops for the footer style (recommended) or for the paragraph.
Code snippet modifying a style:
var style = document.Styles[StyleNames.Footer];
style.ParagraphFormat.TabStops.ClearAll();
style.ParagraphFormat.TabStops.AddTabStop(Unit.FromMillimeter(158), TabAlignment.Right);
You could try something like this:
Paragraph paragraph = new Paragraph();
paragraph.Format.Alignment = ParagraphAlignment.Left;
paragraph.AddText("My custom footer: ");
Paragraph paragraph2 = new Paragraph();
paragraph2.Format.Alignment = ParagraphAlignment.Right;
paragraph2.AddText(" Page # ");
paragraph2.AddPageField();
section.Footers.Primary.Add(paragraph);
section.Footers.Primary.Add(paragraph2);
I am very new to c#/WPF and I need help with what should be a very simple app. In the app I am designing a user simply browses for a .txt or .jpg file which is then loaded. I would like the user to then be able to print what's displayed. The code I have found to work appears to only print part of the contents on one page. In addition, when the .txt files are printed there are no margins and the text seems to go right off the page. Here is the code I am using as it seems to be very basic.
System.Windows.Controls.PrintDialog pd = new System.Windows.Controls.PrintDialog();
if (pd.ShowDialog() != true) return;
pd.PrintVisual(textbox2, "textbox2.");
I guess my question would be, how do I set margin spacing and allow for multiple page document printing? Any help would be greatly appreciated. Thank you.
To print text files you will probably want to use a FlowDocument, this allows you to set margins, pagination etc, e.g.
Printing a WPF FlowDocument
To print a JPEG, you can use PrintVisual, e.g.
Load image from file and print it using WPF... how?
I am trying to save an XPS document with a FixedDocument. So far, I have failed to change the page size. How can I change it to a custom size?
I have based my code on the first answer to this question, more specifically, I am using this code:
if (File.Exists(filename)) {
File.Delete(filename);
}
var oldParent = LogicalTreeHelper.GetParent(this) as ContentControl;
try {
oldParent.Content = null;
FixedDocument doc = new FixedDocument();
PageContent pageCnt = new PageContent();
FixedPage page = new FixedPage();
page.Children.Add(this);
try {
((System.Windows.Markup.IAddChild)pageCnt).AddChild(page);
doc.Pages.Add(pageCnt);
XpsDocument xpsDoc = new XpsDocument(filename, FileAccess.ReadWrite);
try {
var writer = XpsDocument.CreateXpsDocumentWriter(xpsDoc);
writer.Write(doc);
}
finally {
xpsDoc.Close();
}
}
finally {
page.Children.Clear();
}
}
finally {
((ContentControl)oldParent).Content = this;
}
It copies a user control into an XPS document and does so successfully, but, as I said, uses a default paper size.
I have tried to use the DocumentPaginator.PageSize property to set a new page size (after instantiating FixedDocument), but whatever I assign to that property seems to be ignored; the page in the resulting XPS document retains its default paper size.
When executing stepwise, I can see that the value of the PageSize property has really changed, so it's not like the new value somehow isn't accepted by the DocumentPaginator.
I have found various online resources, none of which has solved my problem as yet:
This forum posting at MS Social insists that setting the PageSize property works, but it does not as far as I can tell.
The docs claim that setting the PageSize property works and provide an example which does the same as what I've tried. (Other than that, based on this docs page I can't even tell the unit of the numbers to use.)
The docs also point to the DocumentPage.Size property, however that property cannot be publicly changed. Do I really have to override some page class before adding the page to the document just to get a different page size?
This forum posting describes the same problem, but the answer seems nonsensical to me. I am using the DocumentPaginator property only ever once, so there is no "calling (...).DocumentPaginator again" for which I could save an instance.
This question sounds promising, but it is actually not about the page size, but about the scale of an image on a given page.
Aside from the aforementioned PageSize property (which is set to what seems to be the default size anyway here), this tutorial uses the Width and Height properties of a FixedPage. However, assigning some positive random values to these for a quick test would result in my XPS document apparently being corrupted and XPS Viewer displaying an error message when opening it.
FixedDocuments have fixed pages. The height and width of FixedPage can be controlled. Somewhat like this:
FixedPage pageOne = new FixedPage();
pageOne.Height = 20;
pageOne.Width = 10;
or in XAML:
Height="20" Width="10"
I believe a FixedDocument will only print at the size of its pages. Even when loading a FixedDocument into a DocumentViewer, changing the printer settings' page size when you click the print button will have no effect. A FixedDocument by its very definition preserves the fidelity of the its contents exactly.
The only way to modify it is to create a derived DocumentPaginator which calls the FixedDocument.DocumentPaginator's functions internally and modifies the return values accordingly.
I'm trying to set up the print page for a document, using PageSetupDialog.
Before I open the dialog, the document is set correctly, page size and page source are set correctly too.
But when I return from the dialog after select a different paper size and paper source, the paper size is not correctly reflected, while the paper source is fine. Yes, I am pressing OK button.
This issue is not new but so far there has been no proper answer.
PageSetupDialog dlgPageSetup = new PageSetupDialog();
dlgPageSetup.Document = this.printDocument1; //this is fine, assume that.
dlgPageSetup.PageSettings.PaperSize = new PaperSize("My Custom", 1012, 800);
dlgPageSetup.PageSettings.PaperSource.SourceName = "Envelope";
if (dlgPageSetup.ShowDialog(this) == DialogResult.OK) {
System.Diagnostics.Trace.WriteLine("DEBUG: "
+ dlgPageSetup.PageSettings.PaperSize);
System.Diagnostics.Trace.WriteLine("DEBUG: "
+ dlgPageSetup.PageSettings.PaperSource);
}
I'm using .Net 2.0, VS 2k5.
Link to original issue.
I am guessing this still is a bug, and its related to custom page size. Has anybody got solution for this problem?
I worked around the problem by:
setting the Document property on the pagesettings dialog to (none)
recreating the print document if I see that it is set to a custom PageKind
So before I open the printsettings dialog, I check the PageKind of the print document, recreate if necessary, and then open the dialog.
if(printDocument1->DefaultPageSettings->PaperSize->Kind ==
System::Drawing::Printing::PaperKind::Custom)
{
RecreatePrintDocument();
}
pageSetupDialog1->PageSettings = printDocument1->DefaultPageSettings;
pageSetupDialog1->PrinterSettings = printDocument1->PrinterSettings;
Windows::Forms::DialogResult dresult = pageSetupDialog1->ShowDialog();
In RecreatePrintDocument(), I create a new printdocument and assign the handler, that sort of thing.
This isn't a great solution, because we just forget the page settings if the user chooses a custom page kind, but it's something to start with.
This is a known bug. Till .NET 3.5, it is still reproducible. More details on thread http://social.msdn.microsoft.com/forums/en-US/winforms/thread/81bb2cea-8d47-4ddc-a174-14d6bc196de7/