I am trying to print the content of RichTextBox including the Adorner Layers inside.
I am using this code to print
double w = Editor.ExtentWidth; // Editor is the RichTextBox
double h = Editor.ExtentHeight;
LocalPrintServer ps = new LocalPrintServer();
PrintQueue pq = ps.DefaultPrintQueue;
XpsDocumentWriter xpsdw = PrintQueue.CreateXpsDocumentWriter(pq);
PrintTicket pt = pq.UserPrintTicket;
if (xpsdw != null)
{
pt.PageOrientation = PageOrientation.Portrait;
PageMediaSize pageMediaSize = new PageMediaSize(w, h);
pt.PageMediaSize = pageMediaSize;
xpsdw.Write(Editor);
}
The problem I'm facing is that this code only prints the content that is visible on the screen, not the whole content of the Editor.
EDIT
The pictures are adorner layers, If I print using the method above, it only prints the visible part on the screen not the whole document.
Edit
I'm trying to print each page separately but I cant force Editor.InvalidateVisual(); after doing a Editor.PageDown(); Is there a way I can do that in my method ?
When controls draw on the adorner layer, they search up the tree until they find an adorner layer. Often times this is a the window level. In some cases, you'll want an adorner layer closer to the control, or directly around the control. In this case, wrap the control with an <AdornerDecorator><RichTextBox /></AdornerDecorator>
In your case, you'd probably want to pass a parent element of adorner decorator, or the decorator itself to the print logic. This way the print logic would include the adorner layer as part of the visual. Maybe something like this:
<Grid Name="EditorWrapper">
<AdornerDecorator>
<RichTextBox />
</AdornerDecorator>
</Grid>
Then, pass "EditorWrapper" to the print logic.
EDIT
If you just want to print the contents of the RichTextBox, then you might be best to use the built-in pagination capabilities of the FlowDocument. FlowDocument implements IDocumentPaginatorSource, which will return a paginator that can print the document. Pass that paginator to the XpsDocumentWriter and it should dump the content properly.
var doc = Editor.Document;
var src = doc as IDocumentPaginatorSource;
var pag = src.DocumentPaginator;
xpsdw.Write(pag);
I found this code here:
// Serialize RichTextBox content into a stream in Xaml or XamlPackage format. (Note: XamlPackage format isn't supported in partial trust.)
TextRange sourceDocument = new TextRange(richTextBox.Document.ContentStart, richTextBox.Document.ContentEnd);
MemoryStream stream = new MemoryStream();
sourceDocument.Save(stream, DataFormats.Xaml);
// Clone the source document's content into a new FlowDocument.
FlowDocument flowDocumentCopy = new FlowDocument();
TextRange copyDocumentRange = new TextRange(flowDocumentCopy.ContentStart, flowDocumentCopy.ContentEnd);
copyDocumentRange.Load(stream, DataFormats.Xaml);
// Create a XpsDocumentWriter object, open a Windows common print dialog.
// This methods returns a ref parameter that represents information about the dimensions of the printer media.
PrintDocumentImageableArea ia = null;
XpsDocumentWriter docWriter = PrintQueue.CreateXpsDocumentWriter(ref ia);
if (docWriter != null && ia != null)
{
DocumentPaginator paginator = ((IDocumentPaginatorSource)flowDocumentCopy).DocumentPaginator;
// Change the PageSize and PagePadding for the document to match the CanvasSize for the printer device.
paginator.PageSize = new Size(ia.MediaSizeWidth, ia.MediaSizeHeight);
Thickness pagePadding = flowDocumentCopy.PagePadding;
flowDocumentCopy.PagePadding = new Thickness(
Math.Max(ia.OriginWidth, pagePadding.Left),
Math.Max(ia.OriginHeight, pagePadding.Top),
Math.Max(ia.MediaSizeWidth - (ia.OriginWidth + ia.ExtentWidth), pagePadding.Right),
Math.Max(ia.MediaSizeHeight - (ia.OriginHeight + ia.ExtentHeight), pagePadding.Bottom));
flowDocumentCopy.ColumnWidth = double.PositiveInfinity;
// Send DocumentPaginator to the printer.
docWriter.Write(paginator);
}
Adorner layers are are drawing oriented. So one option left is to convert the entire RichTextBox into a drawing and print that as an Image in XPS.
Although this poses multiple issues...
It will print the external and internal contents that occupy or occupied by the richtextbox i.e. editor toolbar (if it is part of the control template of the rich text box), internal scroll bars etc.
If there are scrollbars then the content out of the scrollbars are not going to be printed as the image will be the exact "snapshot" of the textbox (with remaining text clipped by srollbars).
Will you be happy with that?
I didn't find any way that works 100% for this problem, so I'm trying to transform all my adorner layers to actual images. I'll update the question once I get a 100% working solution.
Related
I would like to add rich text to a TextBox on a PowerPoint Slide.
I am using .Net 4.7.2 with Microsoft.Office.Interop.PowerPoint
Overview
// Initialization of the Application
public PresentationGenerator () {
pptApplication = new Application ();
}
// Creating a new document based on a teplate.
Presentation pptPresentation = generator.pptApplication.Presentations.Open (templatePath, MsoTriState.msoFalse, MsoTriState.msoTrue, showWindow);
// Setting a slide and editing the text works perfectly fine with this
Slide currentSlide;
currentSlide = pptPresentation.Slides[1];
currentSlide.Shapes.Title.TextFrame.TextRange.Text = "Wonderful Title";
currentSlide.Shapes[3].TextFrame.TextRange.Text = "Great TextBox";
This is an mwe of my setup to set text on slides.
I would however like to add text to one of my shapes using a loop and setting the layout depending on a property.
Imagine the following Array
customParagraphs = [
{
text:"example heading",
type:"title"
},{
text:"example normal text",
type:"text"
}]
I can loop over this list and add the text to the end of the TextRange2 using .insertAfter(text) and try setting the font-size for the text portion that i added.
TextRange2 textrange = currentSlide.Shapes[3].TextFrame2.TextRange
foreach(var paragraph in customParagraphs){
TextRange2 paragraphRange = textrange.Paragraphs.insertAfter(paragraph.text)
if(paragraph.type == "title"){
paragraphRange.Font.Size = 24.0F;
}
}
This will successfully add the text and change the font-size, if type is title. However it will change the font-size for the whole text-range!
The reference returned by .insertAfter() seems to refer to the instance of TextRange2 and not my newly added paragraph.
My Questions
Is there a way to change the font-size and other attributes of a line, paragraph or word inside a TextRange or TextRange2 element?
Is there a better way to add text to a TextRange or TextRange2 element than .insertAfter that preferably returns a reference to only the text i added?
I'm using PrintDocument to print out multiple pages that each have one control on. So far I'm able to print them all out on individual sheets as desired, however I'm unable to center the controls in the middle of the pages and it always shows in the top corner of the page. I'll post my code below.
var dialog = new PrintDialog();
var queue = GetPrinterQueue(pPrinterId);
if (queue == null)
return;
dialog.PrintQueue = queue;
dialog.PrintTicket.PageOrientation = paperOrientation
? PageOrientation.Portrait
: PageOrientation.Landscape;
var document = new FixedDocument();
var fixedPage = new FixedPage();
fixedPage.Children.Add(front);
//fixedPage.Measure(size);
//fixedPage.Arrange(new Rect());
//fixedPage.UpdateLayout();
var pageContent = new PageContent();
((IAddChild)pageContent).AddChild(fixedPage);
document.Pages.Add(pageContent);
dialog.PrintDocument(document.DocumentPaginator, "Badge");
That is the bare minimum to make it print out a control per sheet, to make it simpler I have taken out the parts that print to different pages, for the sake of this it only needs to print one.
I've tried changing what is passed into .Arrange() and it makes no difference, what am I missing?
Thanks
I want to display one or more CheckBoxes on a tile in my Windows Phone app. This works already for TextBlocks, but with a CheckBox it shows only the Text of the CheckBox and not the Checkmark itself.
This is a sample of my code:
public void CreateTile()
{
StackPanel panel = new StackPanel();
panel.VerticalAlignment = VerticalAlignment.Top;
panel.Margin = new Thickness(7.0, 7.0, 7.0, 0);
panel.Width = 336;
panel.Height = 336;
panel.Orientation = Orientation.Vertical;
// Create and add a CheckBox for each task
foreach (var task in _tasks)
{
TextBlock textBlock = new TextBlock();
textBlock.TextWrapping = TextWrapping.Wrap;
textBlock.Style = App.Current.Resources["PhoneTextLargeStyle"] as Style;
textBlock.Foreground = new SolidColorBrush(Colors.White);
textBlock.Text = task.Text;
CheckBox checkBox = new CheckBox();
checkBox.IsChecked = task.IsDone;
checkBox.Content = textBlock;
panel.Children.Add(checkBox);
}
Grid layoutRoot = new Grid();
layoutRoot.Background = new SolidColorBrush(Colors.Blue);
layoutRoot.Width = 336;
layoutRoot.Height = 336;
layoutRoot.Children.Add(panel);
layoutRoot.Measure(new Size(336, 336));
layoutRoot.Arrange(new Rect(0, 0, 336, 336));
layoutRoot.UpdateLayout();
// Render grid into bitmap
WriteableBitmap bitmap = new WriteableBitmap(336, 336);
bitmap.Render(layoutRoot, null);
bitmap.Invalidate();
// Save background image for tile to isolated storage
Uri backgroundImage = TileHelper.SaveTileImage(bitmap);
}
If I create a tile with a background image created by the method above, the tile will look like this:
As you can see the text is displayed but there is no checkmark/square before the text.
I personally like to use Segoe UI Symbol as the Font Family in such situations. This gives me the flexibility to use Text and Symbols together while not messing around too much with code / images. SUS has great modern icons (or characters if you may call them) that are very much Metroish, I'd say.
Just open up Charmap (Win + R and type in charmap) and in the Font Select -> Segoe UI Symbol. Now you can select any character you like and paste into Visual Studio Editor itself. Yes, it works!
The symbol may not display properly in the Editor itself but it will at Runtime
Here are some suggestions:
Here are the corresponding characters:
☑
✅
Don't worry about them not looking right HERE. They should when you follow the above steps.
You can always "hack" it by using images of checkbox controls. Did you try to show created control in UI? i.e. adding it to page? Just to see if your code is executed correctly.
Or another solution would be to use check mark character - http://www.fileformat.info/info/unicode/char/2713/index.htm
I'll try to replicate this problem in my test app since it is strange that it does not work.
We've got a large stock of paper forms that we need to fill out. It's very tedious to do this by hand, so we're building an application. It should provide a form to fill in data, be able to show print preview, print the data on the paper form, and keep the history.
Currently, we have a FixedPage which we print like this:
var dlg = new PrintDialog();
if (dlg.ShowDialog() == true)
{
var doc = new FixedDocument();
doc.DocumentPaginator.PageSize = new Size(11.69 * 96, 8.27 * 96); // A4 Landscape
var fp = Application.LoadComponent(new Uri("/FixedPage.xaml", UriKind.Relative)) as FixedPage;
fp.DataContext = this;
fp.UpdateLayout();
var pc = new PageContent();
((IAddChild)pc).AddChild(fp);
doc.Pages.Add(pc);
dlg.PrintTicket.PageOrientation = System.Printing.PageOrientation.Landscape;
dlg.PrintDocument(doc.DocumentPaginator, string.Format("Form #{0}", FormNumber));
}
For the print preview we have a custom UserControl with scanned image of the paper form on background and the data on foreground. Basically, it's repeating the FixedPage layout, and all this makes us think there's a flaw in our design.
Is there a better way to do what we want?
I was tasked with the same problem and wanted to avoid writing my own templating system to save time, unit testing, and my sanity.
I ended up writing a hybrid to did some cool things. First, I wrote my templates using HTML and CSS. It was very easy to do and allowed for great flexibility when making minor adjustments from our Marketing department.
I filled the template with my own tags (e.g [code_type/], [day_list]...[/day_list]) and string replaced the text with a dictionary of tags that could be a single or multivalued.
After generating the html, I would use an html to pdf library I found that uses the open-source webkit engine to render and create the generated pdf. It turned out very stable and took around 2 weeks to write the initial program. Everyone was very pleased and testing was a breeze.
If you want more details, send me a message or reply to this.
We have managed to find a solution, which allows us to throw away a bunch of renundant code. It is still ugly:
public class CustomDocumentViewer : DocumentViewer
{
public static readonly DependencyProperty BackgroundImageProperty =
DependencyProperty.Register("BackgroundImage", typeof(Image), typeof(CustomDocumentViewer), new UIPropertyMetadata(null));
public Image BackgroundImage
{
get { return GetValue(BackgroundImageProperty) as Image; }
set { SetValue(BackgroundImageProperty, value); }
}
protected override void OnDocumentChanged()
{
(Document as FixedDocument).Pages[0].Child.Children.Insert(0, BackgroundImage);
base.OnDocumentChanged();
}
protected override void OnPrintCommand()
{
var printDialog = new PrintDialog();
if (printDialog.ShowDialog() == true)
{
(Document as FixedDocument).Pages[0].Child.Children.RemoveAt(0);
printDialog.PrintDocument(Document.DocumentPaginator, "Test page");
(Document as FixedDocument).Pages[0].Child.Children.Insert(0, BackgroundImage);
}
}
}
...
<local:CustomDocumentViewer x:Name="viewer" BackgroundImage="{StaticResource PaperFormImage}"/>
...
InitializeComponent();
viewer.Document = Application.LoadComponent(new Uri("/PaperFormDocument.xaml", UriKind.Relative)) as IDocumentPaginatorSource;
The reason why we're using Application.LoadComponent instead of binding is a five years old bug: http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=293646
Can someone tell me how to get path geometry from a WPF FlowDocument object? Please note that I do not want to use FormattedText. Thanks.
A FlowDocument can be viewed in any number of ways, but a Path is a fixed shape. I think maybe you really want some simplified, visual-only form of a FlowDocument's contents.
In that case you might try converting the FlowDocument to an XPS FixedDocument - the FixedPages have Canvases containing a bunch of Paths and Glyphs.
Get the Text property of a TextRange object initialized over the entire FlowDocument:
FlowDocument myFlowDocument = new FlowDocument(); //get your FlowDocument
//put in some (or it already has) text
string inText = "Hello, WPF World!";
TextRange tr = new TextRange(FlowDocument.ContentStart, FlowDocument.ContentEnd);
tr.Text = inText;
//get the current text out of the FlowDocument
TextRange trPrime = new TextRange(FlowDocument.ContentStart, FlowDocument.ContentEnd);
string outText = trPrime.Text;
//now outText == "Hello, WPF World!";
//to get formatting, looks like you would use myFlowDocument.TextEffects
Can you use
ChildVisual = VisualTreeHelper.GetChild(Visual yourVisual)
Dunno if you can take a Visual and turn it into a path geometry..