是否有一种简单/内置的方法来获取XAML元素的精确副本(克隆)?

我需要使XAML的区域可打印 ,因此制作此按钮处理程序:

private void Button_Click_Print(object sender, RoutedEventArgs e) { Customer.PrintReport(PrintableArea); } 

在PrintReport中,我将frameworkelement打包到其他元素中,以便以与屏幕上不同的方式打印它,如下所示:

 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"); } } 

但上面给出了以下错误

指定的元素已经是另一个元素的逻辑子元素。 首先断开它。

有没有一种简单的方法来克隆FrameworkElement,以便我可以操作副本,打印它,然后忘记它,让XAML中的原始元素显示在屏幕上完好无损?

我会想象这样的东西:

 FrameworkElement fwe2 = FrameworkElement.Clone(fwe); //pseudo-code 

我在当前的项目中遇到了类似的问题,并使用此代码解决了这个问题。

 public static class ExtensionMethods { public static T XamlClone(this T original) where T : class { if (original == null) return null; object clone; using (var stream = new MemoryStream()) { XamlWriter.Save(original, stream); stream.Seek(0, SeekOrigin.Begin); clone = XamlReader.Load(stream); } if (clone is T) return (T)clone; else return null; } } 

这样它只是作为WPF项目中所有对象的方法出现,您不需要为该方法提供任何参数,它返回与原始对象相同的对象。

在WPF中,复制(或“克隆”)元素几乎永远不正确。 这有效地使这成为XY问题 。 即你只认为你需要在视觉树中克隆元素。 但你没有。

这里惯用且正确的方法是声明一个表示您要打印的数据的DataTemplate 。 当然,这也意味着您要打印的数据又由视图模型类表示,已为其声明了DataTemplate (即通过DataType属性)。

例如:

    

当然, PrintableViewModel类是一个视图模型类,包含要用于填充将要打印的可视树的数据。

在UI的XAML中,您可以使用以下内容:

  

即将Content属性绑定到当前DataContext对象中的属性,该属性返回PrintableViewModel的实例,并让ContentControl适当地显示数据。

WPF将查找相应的数据模板并将其应用于ContentControl显示。 当您想要打印数据时,您只需执行以下操作:

 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"); } 

这将导致WPF自动重用您已经为PrintableViewModel类描述的模板,根据该模板填充ContentControl的可视子树,复制您在屏幕上显示的视觉,但无需执行任何操作一种显式的UI元素克隆。

以上说明了如何准确地重用视觉表示。 但是,当然如果您希望为打印目的定制输出,那么就像在打印时声明要使用的其他DataTemplate一样简单。

我确信应该有简单的方法来复制(除了从父级分离,打印和附加)。 例如,您可以尝试使用XamlWriter来编写xaml,然后通过XamlReader将其读回。 但我怀疑这种方式可能会出现一些绑定和布局错误。

相反,我会尝试使用WriteableBitmap拍摄可打印区域的快照并打印它。 这样你就可以创建光栅和松散的矢量,但是我在打印方面还不够好,无论它是好还是坏。 无论如何你可以尝试检查:)。

干杯,安瓦卡。