WPF的打印预览+用户自定义PDF模板的编写(HTML to PDF)

模板编写

模板准备使用Antlr3.StringTemplate+HtmlRenderer来实现。

接下来实现的是一个下面一个RichTextBox来编辑模板,上面一个HtmlRenderer的HtmlPanel控件来显示模板编辑后的HTML的样式,且每秒刷新一次,然后左边使用固定的包含属性的TreeView,能够将属性格式直接拖到RichTextBox中,方便后台对数据的填充
在这里插入图片描述

编辑模板界面

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="150"/>
        <ColumnDefinition Width="auto"/>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>
    <TreeView x:Name="treeview" Grid.Column="0" PreviewMouseLeftButtonDown="TreeView_MouseDown"></TreeView>
    <GridSplitter Grid.Column="1" HorizontalAlignment="Stretch" Width="4" Background="#BFDBFF" />
    <Grid  Grid.Column="2">
        <Grid.RowDefinitions>
            <RowDefinition Height="*" MinHeight="80" />
            <RowDefinition Height="auto" />
            <RowDefinition Height="150" MinHeight="50" />
        </Grid.RowDefinitions>
        <wpf1:HtmlPanel x:Name="_htmlPanel" Grid.Row="0">
            <wpf1:HtmlPanel.ToolTip>
                <ToolTip BorderBrush="Transparent" Padding="0">
                    <wpf1:HtmlLabel x:Name="_htmlTooltipLabel"/>
                </ToolTip>
            </wpf1:HtmlPanel.ToolTip>
        </wpf1:HtmlPanel>
        <GridSplitter Grid.Row="1" HorizontalAlignment="Stretch" Height="4" Background="#BFDBFF" />
        <xctk:RichTextBox x:Name="_htmlEditor" Grid.Row="2" VerticalScrollBarVisibility="Visible" BorderThickness="0" 
                     TextChanged="OnHtmlEditor_TextChanged" AllowDrop="True" PreviewDrop="_htmlEditor_Drop"/>
    </Grid>
</Grid>
public partial class SetHtmlWindow : Window
{
    private bool _updateLock;
    private readonly Timer _updateHtmlTimer;
    private Dictionary<string, string> _dic = new Dictionary<string, string>() {
        {"$标题$","$Title$" },
        {"$头$","$Head$" },
        {"$身体$","$Body$" },
    };
    public SetHtmlWindow()
    {
        InitializeComponent();
        _updateHtmlTimer = new Timer(OnUpdateHtmlTimerTick);
        treeview.ItemsSource = _dic.Keys;
    }
    public SetHtmlWindow(Dictionary<string, string> dic) : this()
    {
        this._dic = dic;
    }
    /// <summary>
    /// On text change in the html editor update 
    /// </summary>
    private void OnHtmlEditor_TextChanged(object sender, TextChangedEventArgs e)
    {
        if (!_updateLock)
        {
            _updateHtmlTimer.Change(1000, int.MaxValue);
        }
    }
    /// <summary>
    /// Set html syntax color text on the RTF html editor.
    /// </summary>
    private void SetColoredText(string text, bool color)
    {
        var selectionStart = _htmlEditor.CaretPosition;
        _htmlEditor.Text = color ? HtmlSyntaxHighlighter.Process(text) : text.Replace("\n", "\\par ");
        _htmlEditor.CaretPosition = selectionStart;
    }

    /// <summary>
    /// Get the html text from the html editor control.
    /// </summary>
    private string GetHtmlEditorText()
    {
        return new TextRange(_htmlEditor.Document.ContentStart, _htmlEditor.Document.ContentEnd).Text;
    }
    /// <summary>
    /// Update the html renderer with text from html editor.
    /// </summary>
    private void OnUpdateHtmlTimerTick(object state)
    {
        Dispatcher.BeginInvoke(new Action<Object>(o =>
        {
            _updateLock = true;

            try
            {
                _htmlPanel.Text = GetHtmlEditorText();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString(), "Failed to render HTML");
            }

            _updateLock = false;
        }), state);
    }

    private void TreeView_MouseDown(object sender, MouseButtonEventArgs e)
    {
        var treeViewItem = VisualUpwardSearch<TreeViewItem>(e.OriginalSource as DependencyObject) as TreeViewItem;
        string key = treeViewItem.DataContext.ToString();
        DragDrop.DoDragDrop(treeViewItem, key, DragDropEffects.Copy);
    }
    static DependencyObject VisualUpwardSearch<T>(DependencyObject source)
    {
        while (source != null && source.GetType() != typeof(T))
            source = VisualTreeHelper.GetParent(source);

        return source;
    }
    private void _htmlEditor_Drop(object sender, DragEventArgs e)
    {
        if (e.Data.GetDataPresent(typeof(string)))
        {
            string result = e.Data.GetData(typeof(string)) as string;
            string value = _dic[result];
            e.Data.SetData(value);
        }
    }

    private void SaveBtn_Click(object sender, RoutedEventArgs e)
    {
        if (MessageBoxHelper.ShowMessage("The file will be overwritten. Are you sure you want to save it?", "文件将被覆盖,是否确定保存?", (LangType)SysInfoModel.SysInfo.LangType, true))
        {
            var templatePath = @"templates\page.st";
            File.WriteAllText(templatePath, GetHtmlEditorText());
        }
    }

    private void CancleBtn_Click(object sender, RoutedEventArgs e)
    {
        this.Close();
    }

    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        string page = new ReportPage().Generate();

        _htmlEditor.Document.Blocks.Clear();
        _htmlEditor.Document.Blocks.Add(new Paragraph(new Run(page)));

        _htmlPanel.Text = page;
        SetColoredText(GetHtmlEditorText(), true);
    }
}
public abstract class Page
{
    /** My template library */
    protected static StringTemplateGroup templates =
        new StringTemplateGroup("mygroup", "templates");

    public string Generate()
    {
        var templatePath = @"templates\page.st";
        string page = File.ReadAllText(templatePath);
        return page;
    }

    public string Generate(Dictionary<string, string> Dic)
    {
        templates.FileCharEncoding = Encoding.UTF8;
        StringTemplate pageST = templates.GetInstanceOf("page");
        GenerateBody(pageST, Dic);

        string page = pageST.ToString(); // render page
        return page;
    }
    public abstract void GenerateBody(StringTemplate stringTemplate, Dictionary<string, string> Dic);
    protected string Set(string str)
    {
        if (string.IsNullOrEmpty(str))
            return "&nbsp;";
        return str;
    }
}
public class ReportPage : Page
{
    /** This "controller" pulls from "model" and pushes to "view" */
    public override void GenerateBody(StringTemplate stringTemplateDictionary<string,string> Dic)
    {
        stringTemplate.SetAttribute("Title", Set(Dic["Title"]));
        stringTemplate.SetAttribute("Head", Set(Dic["Head"]));
        stringTemplate.SetAttribute("Body", Set(Dic["Body"]));
    }
}

打印预览

模板编辑好了之后从Nuget添加FreeSpire.PDF,它能支持10页之内的PDF文件操作免费
在这里插入图片描述

然后后面是打印预览界面

void Show()
{
    string page = GetHTML();
    string tempXpsPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Helper","temp.xps");
    SpireToXPS(page, tempXpsPath);
    var xpsd = new XpsDocument(tempXpsPath, FileAccess.Read);
    docViewer.Document = xpsd.GetFixedDocumentSequence();
    xpsd.Close();
}
private string GetHTML()
{
    Dictionary<string, string> Dic = new Dictionary<string, string>();
    Dic.Add("Title", "我的大标题");
    Dic.Add("Head", “我的大头”);
    Dic.Add("Body", “我的肚皮”);
    string page = new Windows.ReportPage().Generate(Dic);
    return page;
}
private void SpireToXPS(string htmlCode, string outputPath = "output.xps")
{
    Spire.Pdf.PdfDocument doc = new Spire.Pdf.PdfDocument();
    Spire.Pdf.HtmlConverter.PdfHtmlLayoutFormat htmlLayoutFormat = new Spire.Pdf.HtmlConverter.PdfHtmlLayoutFormat();
    htmlLayoutFormat.IsWaiting = false;
    htmlLayoutFormat.FitToHtml = Spire.Pdf.HtmlConverter.Clip.Both;
    Spire.Pdf.PdfPageSettings setting = new Spire.Pdf.PdfPageSettings();
    setting.Margins = new Spire.Pdf.Graphics.PdfMargins(50);
    setting.Size = Spire.Pdf.PdfPageSize.A4;
    Thread thread = new Thread(() =>
    {
        doc.LoadFromHTML(htmlCode, false, setting, htmlLayoutFormat, true);
    });
    thread.SetApartmentState(ApartmentState.STA);
    thread.Start();
    thread.Join();
    doc.SaveToFile(outputPath, Spire.Pdf.FileFormat.XPS);
    doc.Close();
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
WPF(Windows Presentation Foundation)提供了丰富的控件库,允许开发者定制自己的表格,并灵活地管理表格中的内容和布局信息。下面将介绍如何使用WPF创建自定义表格。 在WPF中,表格主要由Grid控件完Grid控件是WPF中最重要的容器之一,它允许将控件按照行和列的方式布局,用于实现自定义表格。 首先,需要在XAML中定义Grid控件,并设置相关属性。Grid中有两个重要属性:RowDefinitions和ColumnDefinitions,它们表示Grid的行和列定义。例如: ``` <Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" /> </Grid.ColumnDefinitions> </Grid> ``` 上面的代码定义了一个Grid控件,它有两行和三列。接下来可以在Grid中添加控件,定义它们所在的行列位置。例如: ``` <Grid> ... <TextBlock Text="Name:" Grid.Row="0" Grid.Column="0" /> <TextBox Grid.Row="0" Grid.Column="1" /> <TextBlock Text="Age:" Grid.Row="1" Grid.Column="0" /> <TextBox Grid.Row="1" Grid.Column="1" /> </Grid> ``` 上面的代码添加了四个控件,分别是两个TextBlock和两个TextBox。这些控件都分别指定了它们在Grid中的行和列位置。 通过以上步骤,就可以实现一个简单的自定义表格。WPF提供了很多属性可以定义控件的外观和布局信息,开发者可以结合自己的需求灵活地配置表格中的内容和样式。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值