Writing out FlowDocument xaml with namespace using XmlWriter - wpf

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),
...);

Related

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();

Silverlight 4, SetBinding not working

I want to bind the contents of a HyperlinkButton to a resource programmatically, it't not working. This is the code I have so far:
HyperlinkButton Link1 = new HyperlinkButton();
Link1.Style = Application.Current.Resources["LinkStyle"] as Style;
Link1.NavigateUri = new Uri("/Home", UriKind.Relative);
Link1.TargetName = "ContentFrame";
Binding b = new Binding("TabTitles.HomePageTitle");
b.Source = this.Resources["ResourceWrapper"];
Link1.SetBinding(HyperlinkButton.ContentProperty, b);
I get a MethodAccessException
The MethodAccessException is commonly thrown when the public access modifier is missing from a member you want to access. Have you tested the TabTitles property of whatever is held in the "ResourceWrapper" resource? Have the then tested the HomePageTitle property of whatever TablTitles returns?
Note also that if HomePageTitle returns a UIElement you can only place that value once in the visual tree, however my guess is its a string.

WPF flowdocument element names are reset?

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.

Creating a CroppedBitmap at runtime - won't load from resource

I'm trying to write code that will load an image from a resource, and then crop it. This code works when I do all, or part, of it in XAML. I want to switch from all-XAML to all-code, so I can reuse this more than one place, with different Uris.
But when I try to do the same thing in code, I get a DirectoryNotFoundException, because suddenly it starts trying to look for a folder on disk, instead of loading the image from the resource.
If I load the BitmapImage in XAML, and then create a CroppedBitmap in XAML, everything works.
If I load the BitmapImage in XAML, and then write code to create a CroppedBitmap from it, everything works.
If I load the BitmapImage in code, without creating a CroppedBitmap from it, everything works.
But if I load the BitmapImage in code and create a CroppedBitmap in code, it tries to load from the filesystem instead of the resources, and I get a DirectoryNotFoundException.
Code samples are below. I'm sure I'm doing something stupid, but I've run through the whole thing three times now (once in my real app, once in a test app, and once while writing up this question), and I got the same results all three times.
For all of the following code samples, I've created an Images folder inside my project, and added an existing image there called "elf.png", with properties set to defaults (Build Action = "Resource"; Copy to Output Directory = "Do not copy").
Case 1: Both BitmapImage and CroppedBitmap in XAML.
<Window x:Class="WpfApplication8.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Window.Resources>
<BitmapImage x:Key="fullImage" UriSource="Images/elf.png"/>
<CroppedBitmap x:Key="croppedImage" Source="{StaticResource fullImage}"
SourceRect="0 0 240 320"/>
</Window.Resources>
<Image Source="{StaticResource croppedImage}"/>
</Window>
This shows the cropped portion of the bitmap, as expected.
Case 2: BitmapImage in XAML; CroppedBitmap in code-behind.
XAML:
<Window x:Class="WpfApplication8.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Window.Resources>
<BitmapImage x:Key="fullImage" UriSource="Images/elf.png"/>
</Window.Resources>
<Image Name="image"/>
</Window>
Constructor in code-behind:
public Window1()
{
InitializeComponent();
var fullImage = (BitmapImage) FindResource("fullImage");
var croppedImage =
new CroppedBitmap(fullImage, new Int32Rect(0, 0, 240, 320));
image.Source = croppedImage;
}
This also shows the cropped portion of the bitmap, as expected.
Case 3: BitmapImage in code; no CroppedBitmap.
public Window1()
{
InitializeComponent();
var uri = new Uri("Images/elf.png", UriKind.RelativeOrAbsolute);
var fullImage = new BitmapImage(uri);
image.Source = fullImage;
}
This shows the entire bitmap. This isn't what I want, but does tell me that I know how to write C# code to create the right kind of Uri and load a BitmapImage from a resource.
Case 4: BitmapImage and CroppedBitmap in code.
public Window1()
{
InitializeComponent();
var uri = new Uri("Images/elf.png", UriKind.RelativeOrAbsolute);
var fullImage = new BitmapImage(uri);
var croppedImage =
new CroppedBitmap(fullImage, new Int32Rect(0, 0, 240, 320));
image.Source = croppedImage;
}
As far as I can tell, this just puts together the same pieces as before. It uses code that I know will load a BitmapImage from a resource, and code that I know will crop a section from a loaded BitmapImage. But somehow, when the two are put together, it forgets that the resource is there, and tries to load from disk. I get the following exception:
XamlParseException: "Cannot create instance of 'Window1' defined in assembly 'WpfApplication8, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'. Exception has been thrown by the target of an invocation. Error in markup file 'Window1.xaml' Line 1 Position 13."
Inner exception: TargetInvocationException: "Exception has been thrown by the target of an invocation."
Inner exception: DirectoryNotFoundException: "Could not find a part of the path 'C:\svn\WpfApplication8\WpfApplication8\bin\Debug\Images\elf.png'."
The inner-inner-exception stack trace shows that the original exception (the DirectoryNotFoundException) is being thrown by the line that instantiates the CroppedBitmap. I don't know why that line would be trying to read from disk, or why it doesn't work when the as-far-as-I-can-tell-equivalent XAML works fine.
Since I know the XAML is using the parameterless constructors, I also tried the following version, which should be much closer to what the XAML actually does:
public Window1()
{
InitializeComponent();
var uri = new Uri("Images/elf.png", UriKind.RelativeOrAbsolute);
var fullImage = new BitmapImage();
fullImage.BeginInit();
fullImage.UriSource = uri;
fullImage.EndInit();
var croppedImage = new CroppedBitmap();
croppedImage.BeginInit();
croppedImage.Source = fullImage;
croppedImage.SourceRect = new Int32Rect(0, 0, 240, 320);
croppedImage.EndInit();
image.Source = croppedImage;
}
Same exception, this time from the croppedImage.EndInit(); line.
Any ideas on how I can get the all-code version to correctly load the resource and crop the image? What's happening in the XAML version that's different?
The magic turned out to be in the BitmapImage's BaseUri property. BaseUri apparently serves as the "current directory" that UriSource is relative to.
When my BitmapImage was being loaded from XAML, BaseUri was being magically set to "pack://application:,,,/WpfApplication8;component/window1.xaml". When I modified code snippet #4 to explicitly set fullImage.BaseUri to that same value before creating the CroppedBitmap, everything worked.
Why it worked from XAML (and from just-BitmapImage-without-CroppedBitmap)
So where did this magic BaseUri value come from?
BaseUri is part of the IUriContext interface. IUriContext.BaseUri is set in two places in the WPF assemblies, and between my various examples, I managed to hit both of them. No wonder I was confused.
BamlRecordReader.ElementInitialize. The BAML loader automatically sets BaseUri anytime it loads an element that implements IUriContext. This explains why my examples #1 and #2 worked: they were loading from the compiled BAML resource.
Image.UpdateBaseUri (called whenever the Source property is changed). This checks to see if the Source implements IUriContext, and if so, sets its BaseUri. This explains why my example #3 worked: pushing the BitmapImage into the GUI forced it to get the right search path.
It only looks for the image in the EXE resources when BaseUri is set to the magic pack:// URI. Without that (as happens when everything is created in code and not pushed into the GUI), it only looks on disk.
The fix
As noted above, I could hard-code BaseUri. But the BaseUriHelper class provides a better fix:
fullImage.BaseUri = BaseUriHelper.GetBaseUri(this);
This sets fullImage to have the same BaseUri as the window (this). If this is done before creating the CroppedBitmap, everything works.

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