I need to make areas of XAML printable and so have make this button handler:
private void Button_Click_Print(object sender, RoutedEventArgs e)
{
Customer.PrintReport(PrintableArea);
}
And in PrintReport I pack the frameworkelement into other elements in order to print it in a slightly different way than it is on the screen, like this:
public void PrintReport(FrameworkElement fwe)
{
StackPanel sp = new StackPanel();
sp.Children.Add(fwe);
TextBlock tb = new TextBlock();
tb.Text = "hello";
sp.Children.Add(tb);
PrintDialog dialog = new PrintDialog();
if (dialog.ShowDialog() == true)
{
dialog.PrintVisual(sp, "Print job");
}
}
But the above gives me the following error:
Specified element is already the logical child of another element. Disconnect it first.
Is there an easy way to clone the FrameworkElement so that I can manipulate the copy, print it, and then forget about it, leaving the original element in the XAML being displayed on the screen intact?
Something like this I would imagine:
FrameworkElement fwe2 = FrameworkElement.Clone(fwe); //pseudo-code
I'm sure there should be easy way to do the copy (other than detaching from parent, printing and attaching back). For example you could try XamlWriter to write xaml, and then read it back via XamlReader. But I suspect there may be some binding and layout errors this way.
Instead I would try to use WriteableBitmap to take a snapshot of printable area and print it. This way you create raster and loose vector, but I'm not good enough in printing to say if it's good or bad. Anyway you could try and check :).
Cheers, Anvaka.
In WPF, copying (or "cloning") elements is almost never correct. This effectively makes this an XY Problem question. I.e. you only think that you need to literally clone the elements in your visual tree. But you don't.
The idiomatic and correct approach here is to declare a
DataTemplate
that represents the data you want to print. Of course, that also means that the data you want to print is in turn being represented by a view model class, for which theDataTemplate
has been declared (i.e. through theDataType
property).For example:
The
PrintableViewModel
class being, of course, a view model class containing the data you want to use to populate the visual tree that will be printed.In the XAML for your UI, you'd then use it something like this:
I.e. bind the
Content
property to a property in the currentDataContext
object that returns an instance of yourPrintableViewModel
, and let theContentControl
display the data appropriately.WPF will look up the appropriate data template and apply it for display in the
ContentControl
. When you want to print the data, you then just do something like this:This will cause WPF to automatically reuse the template you've already described for the
PrintableViewModel
class, populating theContentControl
's visual sub-tree according to that template, duplicating the visual you're displaying on the screen, but without having to do any sort of explicit cloning of UI elements.The above illustrates how to reuse the visual representation exactly. But of course if you have a desire to customize the output for the purpose of printing, it's as simple as declaring a different
DataTemplate
to be used when you print.I had a similar problem in my current project and solved it with this code.
This way it simply appears as a method on all objects in your WPF project, you do not need to give any parameters to the method, and it returns an object of the same class as the original.