我有几个图形与livecharts2。他们需要能够打印为单独的图表(我可以做PrintDialog.PrintVisual),但也与其他测量在一个文件(这将是变成一个PDF使用PDF打印机驱动程序。
现在,图表显示良好,使用PrintVisual也可以打印,但是当尝试创建DocumentPaginator时,页面保持非常空。我做错了什么?
下面是我的Viewmodel(主要取自livecharts2站点上的示例):
using LiveChartsCore;
using LiveChartsCore.SkiaSharpView;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
namespace TestGraphPrintApp
{
class ViewModel
{
private ICommand _printCommand;
public ISeries[] Series { get; set; }
= new ISeries[]
{
new LineSeries<double>
{
Values = new double[] { 2, 1, 3, 5, 3, 4, 6 },
Fill = null
}
};
public ICommand PrintCommand => _printCommand ?? (_printCommand = new RelayCommand<Visual>((param) =>
{
var dialog = new PrintDialog();
bool? result = dialog.ShowDialog();
if (result.HasValue && result.Value)
{
ChartPaginator paginator = new ChartPaginator(this, new System.Windows.Size(dialog.PrintableAreaWidth, dialog.PrintableAreaHeight));
dialog.PrintDocument(paginator, "test");
}
}, obj => true));
}
}
我知道我不应该在ViewModel中做打印代码,但是对于这个例子来说,它可以。
现在,xaml也主要取自livecharts2示例,并添加了用于打印的datatemplate。
<Window x:Class="TestGraphPrintApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:lvc="clr-namespace:LiveChartsCore.SkiaSharpView.WPF;assembly=LiveChartsCore.SkiaSharpView.WPF"
xmlns:local="clr-namespace:TestGraphPrintApp"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
<local:ViewModel/>
</Window.DataContext>
<Window.Resources>
<DataTemplate x:Key="PrintSampleChartDataTemplate" DataType="{x:Type local:ViewModel}">
<lvc:CartesianChart Series="{Binding Series}" AnimationsSpeed="0" Width="800" Height="600"/>
</DataTemplate>
</Window.Resources>
<StackPanel>
<lvc:CartesianChart Name="Chart" Width="800" Height="380" AnimationsSpeed="0"
Series="{Binding Series}">
</lvc:CartesianChart>
<Button Content="Print" Width="Auto" HorizontalAlignment="Right" Command="{Binding PrintCommand}" CommandParameter="{Binding ElementName=Chart}"/>
</StackPanel>
</Window>
最后,但并非最不重要的是,分页器:
using LiveChartsCore.SkiaSharpView.WPF;
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
namespace TestGraphPrintApp
{
class ChartPaginator : DocumentPaginator
{
private ViewModel _chart;
public ChartPaginator(ViewModel chart, Size pageSize)
{
_chart = chart;
PageSize = pageSize;
}
public override bool IsPageCountValid => true;
public override int PageCount => 1;
public override Size PageSize { get; set; }
public override IDocumentPaginatorSource Source => null;
public override DocumentPage GetPage(int pageNumber)
{
if (!(App.Current.MainWindow.Resources["PrintSampleChartDataTemplate"] is DataTemplate template))
{
throw new Exception("Could not find a DataTemplate for printing.");
}
var contentPresenter = new ContentPresenter
{
Content = _chart,
ContentTemplate = template,
Width = 800,
Height = 600
};
contentPresenter.Measure(this.PageSize);
contentPresenter.Arrange(new Rect(0, 0, this.PageSize.Width, this.PageSize.Height));
contentPresenter.UpdateLayout();
return new DocumentPage(contentPresenter, this.PageSize, new Rect(), new Rect());
}
}
}
1条答案
按热度按时间s6fujrry1#
您必须将
FrameworkElement
添加到可视化树中并在打印它之前呈现它。这样,它将被正确地测量,并且Visual
将被正确地初始化。当然,将PrintDialog.PrintVisual
与已经是可视化树的子元素的现有元素一起使用将省略此要求。一些设计注意事项:
ChartPaginator
不应该显式地依赖于应用程序中定义的资源。相反,将此类依赖关系作为参数(例如构造函数或属性参数)传递。这是因为资源键和资源的实际位置(ResourceDictionary
)等细节应该被隐藏,以增强设计(例如可扩展性)。Window
外部访问静态Application.Current.MainWindow
属性表示有设计气味,通常通过将调用代码移动到主Window
来解决。ViewModel
(在您的ChartPaginator
类型中)没有好处。只要提供适当的DataTemplate
,ContentPresenter
可以显示任何类型(object
)。这将使您的类具有高度可重用性-免费。ViewModel
)实现INotifyPropertyChanged
(即使属性不会改变)溶液
要临时将元素添加到可视化树中,您应该创建一个helper类。此helper将管理可视化元素的生存期。这意味着它将元素插入到可视化树中,然后将其删除。
此帮助器的用法如下所示:
VirtualElementHost.cs
ElementLifetime Scope.cs
因为沿着这条路线走下去会修改现有的可视化树,所以有可能导致布局干扰(短暂但可能可见)。
为了解决这个问题,我建议使用一个自定义的打印对话框,它将输出显示为打印预览,这样你就可以优雅地呈现你想要打印的元素。
以下示例基于上述
VirtualElementHost
,并显示如何使用自定义打印对话框来创建允许显示预览的自定义打印流。该示例还显示了如何在不违反MVVM的情况下实现流,并相应地修改现有类型:
主窗口.xaml.cs
主窗口.xaml
PrintPreviewDialog.xaml.cs
PrintPreviewDialog.xaml
ChartPaginator.cs
ViewModel.cs