我需要制作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
答案 0 :(得分:8)
我在当前的项目中遇到了类似的问题,并使用此代码解决了这个问题。
public static class ExtensionMethods
{
public static T XamlClone<T>(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项目中所有对象的方法出现,您不需要为该方法提供任何参数,并返回与原始对象相同的对象。
答案 1 :(得分:4)
在WPF中,复制(或“克隆”)元素几乎永远不正确。这有效地使这成为XY Problem问题。即你只需想想你需要在视觉树中克隆元素。但你没有。
这里惯用且正确的方法是声明代表您要打印的数据的DataTemplate
。当然,这也意味着您要打印的数据又由视图模型类表示,已为其声明DataTemplate
(即通过DataType
属性)。
例如:
<DataTemplate DataType={x:Type PrintableViewModel}>
<!-- template contents go here -->
</DataTemplate>
PrintableViewModel
类当然是一个视图模型类,包含要用于填充将要打印的可视树的数据。
在用户界面的XAML中,您可以使用以下内容:
<ContentControl Content={Binding PrintableViewModelProperty}/>
即。将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
一样简单。
答案 2 :(得分:-1)
我确信应该有简单的方法来复制(除了从父级分离,打印和附加)。例如,您可以尝试XamlWriter来编写xaml,然后通过XamlReader将其读回来。但我怀疑这种方式可能存在一些绑定和布局错误。
相反,我会尝试使用WriteableBitmap拍摄可打印区域的快照并打印出来。这样你就可以创建光栅和松散的矢量,但是我在打印方面还不够好,无论它是好还是坏。无论如何你可以尝试检查:)。
干杯,安瓦卡。