WPF flowdocument element names are reset? - wpf

I have a flowdocument with a named Span" test1" which I want to replace the contents of programmatically
TextRange tr = new TextRange(this.test1.ContentStart,this.test1.ContentEnd);
Run run = this.test1.Inlines.First() as Run;
run.Text = "replaced text";
This works, however the problem is when doing the update the name of the span is removed (looking at the xaml source). Is this by design? Is there any way to retain the Id's?
Hi, this is the method I use for debugging whats actually in the richtextbox (setting xaml source to textbox)
using (MemoryStream ms = new MemoryStream())
{
tr = new TextRange(this.richTextBox1.Document.ContentStart, this.richTextBox1.Document.ContentEnd);
tr.Save(ms, DataFormats.Xaml);
ms.Position = 0;
using (StreamReader sr = new StreamReader(ms))
{
this.textBox1.Text = sr.ReadToEnd();
}
}
This is the test contents of the richtextbox:
<FlowDocument>
<Paragraph>
This is my first <Span Name="test1">a huge amount of space</Span> between it and the second paragraph?
</Paragraph>
</FlowDocument>

Unfortunately, this is just the way FlowDocuments work (same with tags). The only (very hacky) solution I have found for this is to hide a name in the FontFamily property. This works because this property is a comma delimited list of strings, with the first string that matches an existing font family being used, and the rest being ignored. So if you do something like this:
document.FontFamily = "RealFont1, RealFont2, name=test1";
The whole string will be preserved. You may then access the "name" by searching the FontFamily for "name=".
Again, quite hacky, but after months of trying it was the only solution I could find.

Related

trouble displaying flowdocument

I'm having some problems displaying the contents of a flowdocument in a flowdocumentscrollviewer. I create a generic list that holds a class which contains an int, string and a flowdocument.
In a WPF listbox, I am trying to display the flowdocument in the scrollviewer alongside a button. I use the following function called from the WPF window constructor to populate the listbox
private void populateListBox()
{
foreach(Element el in _notesList)
{
StackPanel sp = new StackPanel();
sp.Orientation = Orientation.Horizontal;
Button b = new Button();
b.Content = el._theID;
sp.Children.Add(b);
FlowDocumentScrollViewer fdsv = new FlowDocumentScrollViewer();
fdsv.MinWidth = 400;
fdsv.Document = el._theDoc;
sp.Children.Add(fdsv);
ListBoxItem lbi = new ListBoxItem();
lbi.Content = sp;
noteList.Items.Add(lbi);
}
}
But the code does not work. There are no errors but the scrollviewers are just blank in the listbox. I also tried storing the classes in an ObservableList and binding to the Document property but that didn't work either.
Any ideas what is happening?
Nevermind. I figured it out.
Further down in the program execution I was copying the flowdocument blocks to a merged document in a foreach statement. This doesn't work even if you use Blocks.ToList(). I eventually found a way to copy the document contents to another document here.

Can the WPF RichTextBox content be serialized without FontFamily and FontSize?

Much like the editor you see on StackOverflow, I want users to be able to specify that parts of their text should be bold, italic, or underline, but I do not want them to be able to set the font size or family; these need to be inherited from somewhere else in the visual tree.
When placing the WPF RichTextBox control into an empty window and providing a few characters of text, the serialized representation of the rich text always includes the FontFamily. e.g., in LINQPad:
void Main()
{
var window = new Window();
var editor = new RichTextBox();
window.Content = editor;
window.ShowDialog();
var rtf = RtfUtility.ConvertToRtf(editor.Document);
rtf.Dump();
}
I entered "HELLO, WORLD!" into the RichTextBox.
{\rtf1\ansi\ansicpg1252\uc1\htmautsp\deff2{\fonttbl{\f0\fcharset0 Times New Roman;}{\f2\fcharset0 Segoe UI;}}{\colortbl\red0\green0\blue0;\red255\green255\blue255;}\loch\hich\dbch\pard\plain\ltrpar\itap0{\lang1033\fs18\f2\cf0 \cf0\ql{\f2 {\ltrch HELLO, WORLD!}\li0\ri0\sa0\sb0\fi0\ql\par}}}
The RTF serialization code is nothing special:
var range = new TextRange(document.ContentStart, document.ContentEnd);
using (var stream = new MemoryStream())
{
range.Save(stream, DataFormats.Rtf);
stream.Position = 0;
using (var reader = new StreamReader(stream, Encoding.UTF8))
return reader.ReadToEnd();
}
The serialized representation references both Times New Roman and Segoe UI, but this is undesirable.
Is it possible to present rich text and inherit the font family and size from elsewhere, as well as serialize it without these properties?
I suppose an alternative is to set FontFamily and FontSize to whatever I want each time the text is deserialized -- but that just seems hacky. I'd also be open to an entirely different solution that does not involve RichTextBox, if that's feasible.
FlowDocument assumes Sergoe UI as default font. This font is in the style for FlowDocument objects.
To change this:
editor.Document.FontFamily = new FontFamily("Times New Roman");
editor.Document.FontSize = 12;
You can also create a style for all FlowDocuments using xaml declarations in App.xaml or programmatically, for each FlowDocument:
var style = new Style(typeof(FlowDocument));
style.Setters.Add(new Setter(FlowDocument.FontFamilyProperty, new FontFamily("Times New Roman")));
style.Setters.Add(new Setter(FlowDocument.FontSizeProperty, 12));
editor.Resources.Add(typeof(FlowDocument), style)
When creating style for a paragraph (new Style(typeof(Paragraph))) you can also change each individual paragraph's settings.
Unfortunately serialization saves FlowDocument's resources and all settings which are not inherited.
You can use a textrange:
TextRange tr2 = new TextRange(mergedDocument.ContentStart, mergedDocument.ContentEnd);
tr2.ApplyPropertyValue(Span.FontFamilyProperty, font);
tr2.ApplyPropertyValue(Span.FontSizeProperty, "24");
tr2.ApplyPropertyValue(List.FontFamilyProperty, font);
tr2.ApplyPropertyValue(List.FontSizeProperty, "24");

The content copied from a richtextbox is not getting saved in my database

Am trying to copy the content of a Richtextbox to another Richtextbox using the below code.
FlowDocument doc = RTB1.Document;
RTB1.Document = new FlowDocument();
RTB2.Document = doc;
But the copied line disappears if i try to save the screen where the RichTextBox(RTB2) is there.
Any help on this will be greatful.
In your code RTB1.Document = new FlowDocument(); will asign a new FlowDocument value to the RTB1.that's why the copied line disappears.
Try this
first you need to include the namespace and add the code below
using System.IO;
using System.Windows.Markup;
MemoryStream ms = new MemoryStream();
XamlWriter.Save(RTB1.Document, ms);
ms.Seek(0, SeekOrigin.Begin);
RTB2.Document = XamlReader.Load(ms) as FlowDocument;
After copying the content from one RichTextBox to another the content used to disappear because the focus was not coming back to the copied RichTextBox.
So the solution i used was to set the focus of RichTextBox2 after copying.
FlowDocument doc = RTB1.Document;
RTB1.Document = new FlowDocument();
RTB2.Document = doc;
RTB2.Focus();

Writing out FlowDocument xaml with namespace using XmlWriter

I've got a collection of data that needs to be converted to a .xaml file that can later be loaded as a FlowDocument into a FlowDocumentReader. I don't directly instantiate Paragraphs, Runs, rather I generate the xaml to create the document later.
What I've tried:
I iterate through the data, creating XElements for Paragraphs, Runs, InlineUIContainers, etc. and build up the FlowDocument structure just fine and then call:
XmlWriter writer = XmlWriter.Create("output.xaml");
flowDocElem.WriteTo(writer);
writer.Close();
In the consuming app, I do this:
flowDocument = XamlReader.Load(xamlFile) as FlowDocument;
flowDocumentReader.Document = flowDocument;
xamlFile.Close();
But the loading fails because it doesn't know what a FlowDocument is. The FlowDocument element looks like so:
<FlowDocument Name="testDoc">
(There's no namespace there to shed light as to what a FlowDocument is when it is read in.)
If I hand edit the .xaml and modify the element to be:
<FlowDocument xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
Name="testDoc">
Then it'll load just fine.
When creating the XElement for the FlowDocument, I've tried to do this:
new XElement("FlowDocument", new XAttribute("Name", "testDoc"), new XAttribute("xmlns", "http://schemas.microsoft.com/winfx/2006/xaml/presentation"));
but that doesn't work either - gives me an error if I try to create the namespace attribute.
I can completely cheat and stuff that xmlns into the element and then call something like
File.WriteAllText("output.xaml", fixedTxt);
but that feels dirty and so I think I'm just plain doing it wrong.
Thoughts?
Update:
While this probably isn't the prescriptive solution to the problem, it does work:
By adding a ParserContext to the XamlReader, I was able to get past the problem with loading the FlowDocument xml.
FileStream xamlFile = new FileStream("output.xaml", FileMode.Open, FileAccess.Read);
XamlReader x = new XamlReader();
ParserContext parserContext = new ParserContext();
parserContext.XmlnsDictionary.Add("","http://schemas.microsoft.com/winfx/2006/xaml/presentation");
flowDocument = XamlReader.Load(xamlFile, parserContext) as FlowDocument;
flowDocumentReader.Document = flowDocument;
xamlFile.Close();
Try using XamlWriter instead of XmlWriter.
If you use XLinq you should try the following:
XNamespace ns = #"http://schemas.microsoft.com/winfx/2006/xaml/presentation";
XNamespace xns = #"http://schemas.microsoft.com/winfx/2006/xaml";
XElement someElement = new XElement(ns + "FlowDocument",
new XAttribute(xns + "Name", name),
...);

Printing a Collection in WPF

Is there any way to print in memory collection or variable size in WPF?
I am using the following code in which I print the ListView control. But when the content is larger than the vertical scroll bar takes over and cuts the content.
PrintDialog printDialog = new PrintDialog();
printDialog.ShowDialog();
printDialog.PrintVisual(lvDocumentSummary, "testing printing!");
To print multiple pages you just need to use a class that implements DocumentPaginator FixedDocument is one of the more complex implementations, FlowDocument is a simpler one.
FlowDocument fd = new FlowDocument();
foreach(object item in items)
{
fd.Blocks.Add(new Paragraph(new Run(item.ToString())));
}
fd.Print();
or
PrintDialog pd = new PrintDialog();
pd.PrintDocument(fd);
FixedDocument supports DataBinding (other than FlowDocument) like any other xaml document. just host the listview in a fixeddocument and display it in a DocumentViewer (which has built-in print support).
however, if your list is too long for one page, FixedDocument does not automatically generate a new page (like flowdocument does). therefore you have to create a new page maually with code, as this cannot be done in pure xaml.
If you want nice printing from WPF you need to build a FixedDocument and print that, unfortunately it can be very complex depending on what you are trying to print.
There's some example code that creates a FixedDocument here: http://www.ericsink.com/wpf3d/B_Printing.html
Here's a 2019 answer. Some of the old answers don't work anymore, eg. FlowDocumentReader doesn't have a Print method.
private void Button_Click(object sender, RoutedEventArgs e)
{
FlowDocument fd = new FlowDocument();
foreach (var item in COLLECTION) //<- put your collection here
{
fd.Blocks.Add(new Paragraph(new Run(item.ToString())));
}
PrintDialog pd = new PrintDialog();
if (pd.ShowDialog() != true) return;
fd.PageHeight = pd.PrintableAreaHeight;
fd.PageWidth = pd.PrintableAreaWidth;
IDocumentPaginatorSource idocument = fd as IDocumentPaginatorSource;
pd.PrintDocument(idocument.DocumentPaginator, "Printing Flow Document...");
}
}
Interesting, Is the ListView virtualized? If it is, the object are not drawn, that is a possibility. Take a look at the Printing example from Petzold.
Here is my solution to this problem. It is kinda shaky but works for my scenario.
I read my collection and transform it into a string. The whole collection now resides in a StringBuilder object. Next, I saw the text/string into a file on the client's machine and then run the notepad process with /p to print the contents of the file.
It works and it prints the contents successfully.
Finally, there is a timer which is called after 5 seconds and which removes the file. Basically within 5 seconds the request is already sent to the printer queue. But a better solution will be to make sure that the print job has been processed this way you will be 100% sure that the job has been performed.

Resources