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 the DataTemplate
has been declared (i.e. through the DataType
property).
For example:
<DataTemplate DataType={x:Type PrintableViewModel}>
<!-- template contents go here -->
</DataTemplate>
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:
<ContentControl Content={Binding PrintableViewModelProperty}/>
I.e. bind the Content
property to a property in the current DataContext
object that returns an instance of your PrintableViewModel
, and let the ContentControl
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:
PrintDialog printDialog = new PrintDialog();
if (printDialog.ShowDialog() == true)
{
ContentControl contentControl = new ContentControl { Content = ((ViewModelClass)DataContext)PrintableViewModelProperty};
// This part with the margins is not strictly relevant to your question per se,
// but it's useful enough to be worth including here for future reference
PageImageableArea area = printDialog.PrintQueue.GetPrintCapabilities(printDialog.PrintTicket).PageImageableArea;
contentControl.Margin = new Thickness(area.OriginWidth, area.OriginHeight,
printDialog.PrintableAreaWidth - area.ExtentWidth - area.OriginWidth,
printDialog.PrintableAreaHeight - area.ExtentHeight - area.OriginHeight);
// This shows retrieving the data template which is declared using the DataType
// property. Of course, if you simply declare a key and reference it explicitly
// in XAML, you can just use the key itself here.
DataTemplateKey key = new DataTemplateKey(typeof(MazeViewModel));
contentControl.ContentTemplate = (DataTemplate)FindResource(key);
printDialog.PrintVisual(contentControl, "MazeGenerator");
}
This will cause WPF to automatically reuse the template you've already described for the PrintableViewModel
class, populating the ContentControl
'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.