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时行高显示异常。
修复步骤
同步克隆文档与原文档的段落属性
替换原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; } }修改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; }设置行堆叠策略为严格模式
修改行高调整事件,强制使用指定行高渲染: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
相关产品推荐
相关产品推荐

