Convert WPF (XAML) Control to XPS Document
Actually after messing around with heaps of different samples, all of which are incredibly convoluted and require the use of Document Writers, Containers, Print Queues and Print Tickets, I found Eric Sinks article about Printing in WPF
The simplified code is a mere 10 lines long
public void CreateMyWPFControlReport(MyWPFControlDataSource usefulData)
{
//Set up the WPF Control to be printed
MyWPFControl controlToPrint;
controlToPrint = new MyWPFControl();
controlToPrint.DataContext = usefulData;
FixedDocument fixedDoc = new FixedDocument();
PageContent pageContent = new PageContent();
FixedPage fixedPage = new FixedPage();
//Create first page of document
fixedPage.Children.Add(controlToPrint);
((System.Windows.Markup.IAddChild)pageContent).AddChild(fixedPage);
fixedDoc.Pages.Add(pageContent);
//Create any other required pages here
//View the document
documentViewer1.Document = fixedDoc;
}
My sample is fairly simplistic, it doesn't include Page Sizing and Orientation which contains a whole different set of issues that don't work as you would expect.
Nor does it contain any save functionality as MS seem to have forgotten to include a Save button with the Document Viewer.
Save Functionality is relatively simple (and is also from Eric Sinks article)
public void SaveCurrentDocument()
{
// Configure save file dialog box
Microsoft.Win32.SaveFileDialog dlg = new Microsoft.Win32.SaveFileDialog();
dlg.FileName = "MyReport"; // Default file name
dlg.DefaultExt = ".xps"; // Default file extension
dlg.Filter = "XPS Documents (.xps)|*.xps"; // Filter files by extension
// Show save file dialog box
Nullable<bool> result = dlg.ShowDialog();
// Process save file dialog box results
if (result == true)
{
// Save document
string filename = dlg.FileName;
FixedDocument doc = (FixedDocument)documentViewer1.Document;
XpsDocument xpsd = new XpsDocument(filename, FileAccess.ReadWrite);
System.Windows.Xps.XpsDocumentWriter xw = XpsDocument.CreateXpsDocumentWriter(xpsd);
xw.Write(doc);
xpsd.Close();
}
}
So the answer is Yes, you can take an Existing WPF (XAML) Control, databind it and turn it into an XPS document - and its not all that difficult.
Converting Word Document to XPS Document in C# WPF
I wrote an static method that do what you need.
static void SearchDocuments(string directoryPath)
{
try
{
foreach (string fullName in System.IO.Directory.GetFiles(directoryPath,"*.docx")) // if your word documents come from an older version of word, they might have .doc extenstion
{
convertWordToXps(System.IO.Path.GetDirectoryName(fullFileName), System.IO.Path.GetFileNameWithoutExtension(fullFileName));
}
foreach (string nestedDirectory in System.IO.Directory.GetDirectories(directoryPath))
{
SearchDocuments(nestedDirectory);
}
}
catch (System.Exception error)
{
//Do whatever u want on exception
}
}
Btw. think over if you have reason to use static functions.
Print documents via Wpf-controls and convert to XPS
I'm skipping your second question as it is complex enough to be a standalone.
I faced the same issue, but it may be caused by a couple of different things.
If the issue is because the bindings haven't been "tripped" yet, the solution is slightly hacky but is easy to do if you control the DataContext type. You just add a public or internal method to your type that allows you to fire off PropertyChanged events for each public property. Here's an example:
public interface IForceBinding : INotifyPropertyChanged
{
void ForceBindings();
}
public class MyDataContext : IForceBinding
{
public event PropertyChanged;
private string _text;
public string Text
{
get{return _text;}
set{_text = value; OnPropertyChanged("Text");}
}
public void ForceBindings()
{
OnPropertyChanged("Text");
}
private void OnPropertyChanged(string propertyName)
{
// you know the drill
}
}
then, you can use it thusly:
public void Print(MyDataContext preconfiguredContext){
var page = new MyWpfPage();
page.DataContext = preconfiguredContext;
preconfiguredContext.ForceBindings();
// write to xps
If that isn't working, you might be encountering a bug where the bindings on the first page never show up. I'd have to dig for awhile before I can re-find the solution to that.
How to adjust path of XPS document for wpf Application to work on another PC?
You should get the base directory for your running application which you can append to the relative path to the XPS file. This should work even if you deploy the application as long as you have the XPS file in an Assets folder next to the executable.
string xpsFilePath = Path.Combine(
AppDomain.CurrentDomain.BaseDirectory,
@"Assets\Dokhna - Problems - Report.xps");
XpsDocument myDoc = new XpsDocument(xpsFilePath, FileAccess.Read);
How to create XPS file silently?
You can write any UI element directly to an XPS document without using the "print" functionality. Use the XpsDocument
and XpsDocumentWriter
classes as dictated here.
How to serialize multiple xaml elements into single xps file and print WPF Application c#
Finally lots of digging later found out a solution.
In WPF Application, we can serialize multiple xaml elements individually. All the serialized xaml elements will be in .xps format. Then we can collect back all .xps file and form a single .xps file. This way, we can serialize multiple xaml elements. Thanks.
Center a Control when converting to XPS
So basically the FixedPage
was responsible for all my troubles.
It always resized my control to an area larger than the area available from my PrintDialog
.
I was under the impression that it was necessary to print a any UI element, since every solution I found online uses it, but I found out I can simply print my control directly.
I had to resize the control a little before printing, but once I removed the FixedPage
, this was fairly easy.
control.UpdateLayout();
control.Width = PrintableSize.Width;
control.Height = PrintableSize.Height;
string tempFilename = fileName + "_temp.xps";
XpsDocument xpsDoc = new XpsDocument(tempFilename, FileAccess.Write);
XpsDocumentWriter xWriter = XpsDocument.CreateXpsDocumentWriter(xpsDoc);
xWriter.Write(control);
xpsDoc.Close();
Related Topics
Filter All Queries (Trying to Achieve Soft Delete)
Convert Datatable to Generic List
Executenonquery for Select SQL Statement Returning No Rows
Calculate the Display Width of a String in C#
Declaring a Variable Inside or Outside an Foreach Loop: Which Is Faster/Better
How to Get a List of Keys from JSON.Net
ASP.NET Core Appsettings.JSON Update in Code
Who Should Call Dispose on Idisposable Objects When Passed into Another Object
Should I Unsubscribe from Events
.Include() VS .Load() Performance in Entityframework
C# Get a Control's Position on a Form
Query String Not Working While Using Attribute Routing
How to Route Images Using ASP.NET MVC Routing
What's a Good Threadsafe Singleton Generic Template Pattern in C#
Selectively Suppress Custom Obsolete Warnings
C# Generic "Where Constraint" with "Any Generic Type" Definition