c# - 如何强制 FlowDocument 刷新其在 DataContext 集上的内容?

标签 c# wpf

我正在尝试将 FlowDocument 与绑定(bind)一起使用,以获得单独的模板,该模板能够填充来自数据模型的实际数据。然后我将它转换为图像并打印或保存在硬盘上。 为了将 FlowDocument 的运行与数据模型绑定(bind),我使用了本文中的代码:https://msdn.microsoft.com/en-us/magazine/dd569761.aspx

FlowDocument 模板如下:

<FlowDocument xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
          xmlns:p="clr-namespace:Labels;assembly=DataModel.Impl"
PageWidth="200" MinPageWidth="200" PageHeight="200" MinPageHeight="200">

<Section>
  <Paragraph>
    <p:BindableRun BoundText="{Binding Path=Text}"/>
  </Paragraph>
</Section>

</FlowDocument>

BindableRun 代码:

public class BindableRun : Run
  {
    public static readonly DependencyProperty BoundTextProperty = DependencyProperty.Register("BoundText", typeof(string), typeof(BindableRun),
      new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure, OnBoundTextChanged, CoerceText));

    public BindableRun()
    {
      FlowDocumentHelpers.FixupDataContext(this);
    }

    private static void OnBoundTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
      ((Run)d).Text = (string)e.NewValue;
    }

    private static object CoerceText(DependencyObject d, object value)
    {
      return value;
    }

    public String BoundText
    {
      get { return (string)GetValue(BoundTextProperty); }
      set { SetValue(BoundTextProperty, value); }
    }
  }

然后我加载模板并在其中设置 DataContext:

private class DataClass
{
  public string Text { get; set; }
}

private static FlowDocument LoadFlowDocument(string path)
{
  using (var xamlFile = new FileStream(path, FileMode.Open, FileAccess.Read))
  {
    return XamlReader.Load(xamlFile) as FlowDocument;
  }
}

    private static void FlowDoc2Image(FlowDocument document, DataClass dataContext, Stream imageStream)
{
  var flowDocumentScrollViewer = new FlowDocumentScrollViewer
  {
    VerticalScrollBarVisibility = ScrollBarVisibility.Hidden,
    HorizontalScrollBarVisibility = ScrollBarVisibility.Hidden,
    DataContext = dataContext
  };

  flowDocumentScrollViewer.Document = document;

  flowDocumentScrollViewer.Measure(new Size(999999999, 999999999));

  //1st pass
  flowDocumentScrollViewer.Arrange(new Rect(0, 0, flowDocumentScrollViewer.ActualWidth, flowDocumentScrollViewer.ActualHeight));
  //2nd pass. It's not code duplication! Do not remove!
  flowDocumentScrollViewer.Arrange(new Rect(0, 0, flowDocumentScrollViewer.ActualWidth, flowDocumentScrollViewer.ActualHeight));

  var bitmapRenderer =
    new RenderTargetBitmap((int)flowDocumentScrollViewer.ActualWidth, (int)flowDocumentScrollViewer.ActualHeight, 96, 96, PixelFormats.Pbgra32);

  bitmapRenderer.Render(flowDocumentScrollViewer);

  var pngEncoder = new PngBitmapEncoder { Interlace = PngInterlaceOption.On };
  pngEncoder.Frames.Add(BitmapFrame.Create(bitmapRenderer));

  pngEncoder.Save(imageStream);
}

public void Test()
{
  var doc = LoadFlowDocument("C:\\Experiments\\DocWithBinding.xaml");

  var context = new DataClass {Text = "SomeText"};
  doc.DataContext = context;

  using (var imageStream = new FileStream("C:\\Experiments\\image.png", FileMode.OpenOrCreate, FileAccess.Write))
  {
    FlowDoc2Image(doc, context, imageStream);
  }
}

但是没有任何反应。我试图在 BindableRun 中设置断点来改变它的值。我从来没有到过那里。更改 DataContext 不会影响文档。

最佳答案

不再需要 BindableRun 类。来自 Run.Text 中的备注部分:

Starting in the .NET Framework 4, the Text property of the Run object is a dependency property, which means that you can bind the Text property to a data source.

因此您的 FlowDocument 文件可能如下所示:

<FlowDocument xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    PageWidth="200" MinPageWidth="200" PageHeight="200" MinPageHeight="200">
    <Section>
        <Paragraph>
            <Run Text="{Binding Text}"/>
        </Paragraph>
    </Section>
</FlowDocument>

我已经按照你的问题加载了它,将一个 DataClass 实例分配给它的 DataContext 并成功地将它显示在 RichTextBox 中:

<Grid>
    <RichTextBox x:Name="rtb"/>
</Grid>

代码隐藏:

private class DataClass
{
    public string Text { get; set; }
}

public MainWindow()
{
    InitializeComponent();

    var doc = LoadFlowDocument("DocWithBinding.xaml");
    doc.DataContext = new DataClass { Text = "Hello, World." };
    rtb.Document = doc;
}

private static FlowDocument LoadFlowDocument(string path)
{
    using (var xamlFile = new FileStream(path, FileMode.Open, FileAccess.Read))
    {
        return XamlReader.Load(xamlFile) as FlowDocument;
    }
}

编辑 虽然您能够成功地将 FlowDocument 放入 FlowDocumentScrollViewer,但似乎将此查看器同步渲染到 RenderTargetBitmap 中并不会创建所需的输出。感觉绑定(bind)尚未建立,因为文档中的硬编码文本将同步呈现。

我已经尝试了一些方法,但我似乎无法避免在渲染到位图之前添加一个短暂的延迟。我通过使 FlowDoc2Image 方法异步并调用 await Task.Delay(100) 来做到这一点。这是一个 hack,但它创建了 PNG。

private async Task FlowDoc2Image(
    FlowDocument document, DataClass dataContext, Stream imageStream)
{
    var flowDocumentScrollViewer = new FlowDocumentScrollViewer
    {
        VerticalScrollBarVisibility = ScrollBarVisibility.Hidden,
        HorizontalScrollBarVisibility = ScrollBarVisibility.Hidden,
        Document = document,
        DataContext = dataContext,
    };

    flowDocumentScrollViewer.Measure(
        new Size(double.PositiveInfinity, double.PositiveInfinity));
    flowDocumentScrollViewer.Arrange(new Rect(flowDocumentScrollViewer.DesiredSize));

    await Task.Delay(100);

    var renderTargetBitmap = new RenderTargetBitmap(
        (int)flowDocumentScrollViewer.DesiredSize.Width,
        (int)flowDocumentScrollViewer.DesiredSize.Height,
        96, 96, PixelFormats.Default);

    renderTargetBitmap.Render(flowDocumentScrollViewer);

    var pngEncoder = new PngBitmapEncoder { Interlace = PngInterlaceOption.On };
    pngEncoder.Frames.Add(BitmapFrame.Create(renderTargetBitmap));
    pngEncoder.Save(imageStream);
}

关于c# - 如何强制 FlowDocument 刷新其在 DataContext 集上的内容?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28835884/

相关文章:

c# - 手动实例化和调用 TagHelpers

c# - 提高 C# 循环的效率

c# - 添加日志记录的更好方法或需要在每个函数中出现的方法?

.net - 如何在 Visual Studio 2010 中引用 WPF 工具包?

c# - DataGrid AutoGenerateColumns ="True"- 如何追加一列?

c# - 如何在不解压的情况下递归探索 zip 文件内容

c# - AutoMapper:两个集合之间的映射与 'Ignore'

c# - Windows 7 更新,导致 WPF 应用程序中出现 "Item has already been added. Key in dictionary: controlbrush"

c# - 列表框项目中的第一个元素与第二个元素重叠

wpf - 通过重新查询或添加到集合来更新可观察集合?