You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WPF RichTextBox转PNG行高显示异常的技术求助

解决WPF RichTextBox导出PNG时行高显示错误问题

开发WPF富文本编辑器,基础功能(字体、字号、颜色、行间距、背景图)正常,但导出PNG时,调整过行高的内容显示错误,尝试现有方案未解决。以下是相关代码及修复方案:


现有代码片段

XAML布局代码

<ToolBar Name="mainToolBar" Height="30" DockPanel.Dock="Top">
    <Slider x:Name="slider01" HorizontalAlignment="Left"  VerticalAlignment="Top" Width="60" Cursor="Arrow" ValueChanged="comboLineHeight_ValueChanged" SmallChange="10" TickPlacement="TopLeft" Foreground="Black" Background="#FFCACACA" IsSnapToTickEnabled="True" Maximum="400" Minimum="1" />                    
</ToolBar>

<RichTextBox Name="mainRTB" FontSize="20" AcceptsTab="True" FontFamily="Arial" >
    <RichTextBox.Resources>
        <Style TargetType="{x:Type Paragraph}">
            <Setter Property="Margin" Value="0"/>
            <Setter Property="Padding" Value="0"/>
        </Style>
    </RichTextBox.Resources>
    <FlowDocument>

    </FlowDocument>
</RichTextBox>

生成Bitmap的方法

public BitmapSource FlowDocumentToBitmap(FlowDocument document, Size size)
{
    //Make a copy
    document = CloneDocument(document);

    //Set margins to zero otherwise they will be "Auto"
    SetZeroMargin(document);

    document.ColumnWidth = size.Width;

    document.PagePadding = new Thickness(0, 0, 0, 0);

    var paginator = ((IDocumentPaginatorSource)document).DocumentPaginator;
    paginator.PageSize = size;

    var visual = new DrawingVisual();
    using (var drawingContext = visual.RenderOpen())
    {
        if (myBackground != null)
        {
            imgBrush.ImageSource = new BitmapImage(new Uri(myBackground));

            drawingContext.DrawImage(imgBrush.ImageSource, new Rect(size));
        }
    }
    visual.Children.Add(paginator.GetPage(0).Visual);

    var bitmap = new RenderTargetBitmap((int)size.Width, (int)size.Height, 96, 96, PixelFormats.Pbgra32);

    bitmap.Render(visual);
    return bitmap;
}

克隆文档方法

public FlowDocument CloneDocument(FlowDocument document)
{
    var copy = new FlowDocument();
    var sourceRange = new TextRange(document.ContentStart, document.ContentEnd);
    var targetRange = new TextRange(copy.ContentStart, copy.ContentEnd);

    using (var stream = new MemoryStream())
    {
        sourceRange.Save(stream, DataFormats.XamlPackage);
        targetRange.Load(stream, DataFormats.XamlPackage);
    }

    return copy;
}

段落边距重置方法

void SetZeroMargin(FlowDocument flowDoc)
{
    foreach (Block block in flowDoc.Blocks)
    {
        if (typeof(Paragraph).IsAssignableFrom(block.GetType()))
        {
            Paragraph p = (Paragraph)block;
            p.Margin = new Thickness(0, 0, 0, 0);
        }
    }
}

行高调整事件

private void comboLineHeight_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
Slider slider = (Slider)sender;

    foreach (Paragraph p in GetSelectedLines())
    {
        p.LineHeight = Convert.ToDouble(slider.Value);
    }

}

问题根源与修复方案

问题原因

克隆文档时,DataFormats.XamlPackage未完整保留段落的LineHeight和LineStackingStrategy属性,且分页渲染时默认的行堆叠策略会覆盖自定义行高设置,导致导出PNG时行高显示异常。

修复步骤

  1. 同步克隆文档与原文档的段落属性
    替换原SetZeroMargin方法,改为同步原文档的关键布局属性:

    void SyncParagraphProperties(FlowDocument originalDoc, FlowDocument clonedDoc)
    {
        var originalParagraphs = originalDoc.Blocks.OfType<Paragraph>().ToList();
        var clonedParagraphs = clonedDoc.Blocks.OfType<Paragraph>().ToList();
    
        for (int i = 0; i < Math.Min(originalParagraphs.Count, clonedParagraphs.Count); i++)
        {
            var originalPara = originalParagraphs[i];
            var clonedPara = clonedParagraphs[i];
    
            // 同步边距
            clonedPara.Margin = new Thickness(0);
            // 同步行高和行堆叠策略
            clonedPara.LineHeight = originalPara.LineHeight;
            clonedPara.LineStackingStrategy = originalPara.LineStackingStrategy;
            // 同步内边距
            clonedPara.Padding = originalPara.Padding;
        }
    }
    
  2. 修改Bitmap生成方法,传入原文档同步属性
    调整FlowDocumentToBitmap的参数,传入原始文档并调用新的同步方法:

    public BitmapSource FlowDocumentToBitmap(FlowDocument originalDocument, Size size)
    {
        // 克隆文档
        var document = CloneDocument(originalDocument);
    
        // 同步段落属性
        SyncParagraphProperties(originalDocument, document);
    
        document.ColumnWidth = size.Width;
        document.PagePadding = new Thickness(0);
    
        var paginator = ((IDocumentPaginatorSource)document).DocumentPaginator;
        paginator.PageSize = size;
    
        var visual = new DrawingVisual();
        using (var drawingContext = visual.RenderOpen())
        {
            if (myBackground != null)
            {
                imgBrush.ImageSource = new BitmapImage(new Uri(myBackground));
                drawingContext.DrawImage(imgBrush.ImageSource, new Rect(size));
            }
        }
        visual.Children.Add(paginator.GetPage(0).Visual);
    
        var bitmap = new RenderTargetBitmap((int)size.Width, (int)size.Height, 96, 96, PixelFormats.Pbgra32);
        bitmap.Render(visual);
        return bitmap;
    }
    
  3. 设置行堆叠策略为严格模式
    修改行高调整事件,强制使用指定行高渲染:

    private void comboLineHeight_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
    {
        Slider slider = (Slider)sender;
        foreach (Paragraph p in GetSelectedLines())
        {
            p.LineHeight = slider.Value;
            // 强制使用设置的行高,禁止自动计算
            p.LineStackingStrategy = LineStackingStrategy.BlockLineHeight;
        }
    }
    

原理说明

  • LineStackingStrategy.BlockLineHeight会让段落严格按照设置的LineHeight渲染每行,避免自动调整导致的行高偏差。
  • 手动同步原文档的行高和堆叠策略,确保克隆后的文档与RichTextBox内的布局完全一致,解决克隆过程中属性丢失的问题。

内容的提问来源于stack exchange,提问作者cesar bueno

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 13:40:24