// PrintPdfViewModel.cs
public class PrintPdfViewModel : BindableBase, IDialogAware
{
private string _template;
///
/// PDF 的 html 模板
///
public string Template
{
get => _template;
set => SetProperty(ref _template, value);
}
private ExpandoObject _data;
/// <summary>
/// 传递给 pdf 的数据
/// </summary>
public ExpandoObject Data
{
get => _data;
set => SetProperty(ref _data, value);
}
public void OnDialogOpened(IDialogParameters parameters)
{
// 弹窗接收 template 和 data 两个参数
parameters.TryGetValue("template", out _template);
parameters.TryGetValue("data", out _data);
}
public string Title => "预览 PDF";
}
使用 vue 来定义 pdf 模板的逻辑,和调用 WebView2.CoreWebView2.PrintToPdfAsync 来生成 PDF。
var assembly = Assembly.GetExecutingAssembly();
var resourceName = "PrintPdf.Views.vue.global.js";
using var stream = assembly.GetManifestResourceStream(resourceName);
if (stream != null)
{
using var reader = new StreamReader(stream);
var vue = await reader.ReadToEndAsync();
await webView2.CoreWebView2.AddScriptToExecuteOnDocumentCreatedAsync(vue); // 加载 vuejs
}
var vm = (PrintPdfViewModel)DataContext;
webView2.CoreWebView2.NavigateToString(vm.Template); // 加载 pdf 模板
webView2.CoreWebView2.NavigationCompleted += (sender, args) =>
{
var json = JsonSerializer.Serialize(vm.Data);
webView2.CoreWebView2.PostWebMessageAsJson(json); // 将数据传递到 html 中
};
}
点击保存时,选择路径并生成 PDF 文件。
// PrintPdfViewModel.xaml.cs
save.Click += async (sender, args) =>
{
var saveFileDialog = new SaveFileDialog
{
Filter = "txt files (.pdf)|.pdf",
RestoreDirectory = true,
InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
FileName = $"test.pdf"
};
var result = saveFileDialog.ShowDialog();
if (result != true)
return;
var printSetting = webView2.CoreWebView2.Environment.CreatePrintSettings();
printSetting.ShouldPrintBackgrounds = true;
var saveResult = await webView2.CoreWebView2.PrintToPdfAsync($"{saveFileDialog.FileName}", printSetting);
在 WPF 客户端点击生成 PDF 时,打开 PDF 预览窗口,并且传递模板和数据给 WebView2
// BuyBookView.xaml
// BuyBookViewModel
ShowPrintViewCommand = new DelegateCommand(() =>
{
var assembly = Assembly.GetExecutingAssembly();
var resourceName = $"PrintPdf.ViewModels.test_print.html";
using var stream = assembly.GetManifestResourceStream(resourceName); // 加载模板
if (stream == null) return;
using var reader = new StreamReader(stream);
var t = reader.ReadToEnd();
dynamic d = new ExpandoObject(); // 转换数据
d.title = Title;
d.books = Books;
var p = new DialogParameters
{
{"template", t},
{"data", d}
};
dialogService.ShowDialog(nameof(PrintPdfView), p, null);
});
现在功能已经差不多了,但是 html 模板需要写的 js 太多,而且这是一个 WPF 控件,所以应该封装一下,最好用起来跟 wpf 一样才更好。
var assembly = Assembly.GetExecutingAssembly();
var controlsFile = "PrintPdf.Views.controls.js";
using var controlsStream = assembly.GetManifestResourceStream(controlsFile);
using var controlsReader = new StreamReader(controlsStream);
var controls = await controlsReader.ReadToEndAsync();
await webView2.CoreWebView2.AddScriptToExecuteOnDocumentCreatedAsync(controls);