将图表(从网格)导出为BMP

3

我希望能从网格中将每个图表导出为系统中的图像文件。

以下是XAML和C#的代码。

问题在于,当我导出时,只有第一个图表能够正确地导出,而第二个图表无法导出,因为它没有映射到第二个图表上。

以下是屏幕截图。

XAML:

  <TabItem x:Name="Charts" Header="Company Charts " TabIndex="0" IsSelected="True">
                        <ScrollViewer ScrollViewer.VerticalScrollBarVisibility="Auto" ScrollViewer.HorizontalScrollBarVisibility="Auto" Background="Transparent" telerik:StyleManager.Theme="Expression_Dark">
                            <Grid>
                                <Grid x:Name="ChartGrid"  Margin="10" Background="Black">
                                    <Grid.Resources>

                                        <telerik:RadContextMenu x:Key="context" Width="100" telerik:StyleManager.Theme="Expression_Dark">

                                            <telerik:RadContextMenu.Background>
                                                <LinearGradientBrush EndPoint="0.5,1" MappingMode="RelativeToBoundingBox" StartPoint="0.5,0">
                                                    <GradientStop Color="#FF515151" Offset="0.021"/>
                                                    <GradientStop Color="#FF212020" Offset="0.979"/>
                                                    <GradientStop Color="#FF222121" Offset="0.115"/>
                                                </LinearGradientBrush>

                                            </telerik:RadContextMenu.Background>
                                            <telerik:RadMenuItem Header="View Data" Foreground="White" Command="{Binding ViewData}" CommandParameter="{Binding RelativeSource={RelativeSource Self}}" telerik:StyleManager.Theme="Expression_Dark"></telerik:RadMenuItem>
                                            <telerik:RadMenuItem Header="Edit Chart" Foreground="White" Command="{Binding EditChartCmd}" CommandParameter="{Binding RelativeSource={RelativeSource Self}}" telerik:StyleManager.Theme="Expression_Dark"></telerik:RadMenuItem>
                                        </telerik:RadContextMenu>

                                        <DataTemplate x:Key="EmptyContentTemplate">
                                            <StackPanel Orientation="Horizontal">
                                                <TextBlock Text="N.A." Margin="5,15,0,0" VerticalAlignment="Center" Height="30" Foreground="{Binding LabelFG}"></TextBlock>
                                            </StackPanel>
                                        </DataTemplate>

                                        <!--<DataTemplate x:Key="LegendOrientation">
                                        <StackPanel Orientation="Horizontal" />
                                    </DataTemplate>-->
                                    </Grid.Resources>
                                    <Grid.ColumnDefinitions>
                                        <ColumnDefinition Width="*"></ColumnDefinition>
                                        <ColumnDefinition Width="*"></ColumnDefinition>
                                        <ColumnDefinition Width="*"></ColumnDefinition>
                                    </Grid.ColumnDefinitions>
                                </Grid>
                            </Grid>
                        </ScrollViewer>
                    </TabItem>

C# 代码:

 private void ExportCHARTtoBMP(RadCartesianChart chartx, string p)
    {
        chartx.Measure(new System.Windows.Size(double.PositiveInfinity, double.PositiveInfinity));
        int chartW = (int)Math.Round(chartx.ActualWidth);
        int chartH = (int)Math.Round(chartx.ActualHeight);
        chartW = chartW == 0 ? 1 : chartW;
        chartH = chartH == 0 ? 1 : chartH;

        RenderTargetBitmap rtbmp = new RenderTargetBitmap(chartW + 32, chartH + 20, 96d, 96d, PixelFormats.Default);
        rtbmp.Render(chartx);
        BmpBitmapEncoder encoder = new BmpBitmapEncoder();
        encoder.Frames.Add(BitmapFrame.Create(rtbmp));

        FileStream chartFS = File.Create(tempPath + "" + p + ".bmp");
        encoder.Save(chartFS);
        chartFS.Close();
        rtbmp.Clear();
    }

private void charttoimageprocess(List<string> axisdata1, List<string> axisdata2)
    {
        for (int row = 0; row < (chartgridimage.RowDefinitions.Count()); row++)
        {

            for (int column = 0; column < chartgridimage.ColumnDefinitions.Count(); column++)
            {
                RadCartesianChart chart = chartgridimage.ChildrenOfType<RadCartesianChart>().FirstOrDefault(e => Grid.GetRow(e) == row && Grid.GetColumn(e) == column);
                chart.UpdateLayout();
                chart.Arrange(new Rect(new System.Windows.Size(chart.ActualWidth, chart.ActualHeight)));
                chart.UpdateLayout();
                ExportCHARTtoBMP(chart, "chart" + row + "" + column);
                chart_names.Add("chart" + row + "" + column + ".bmp");
                StackPanel stackpan1 = chartgridimage.ChildrenOfType<StackPanel>().FirstOrDefault(e => Grid.GetRow(e) == row && Grid.GetColumn(e) == column && e.Name.Equals("stackpan1"));
                Telerik.Windows.Controls.Label label1 = stackpan1.ChildrenOfType<Telerik.Windows.Controls.Label>().FirstOrDefault(e => e.Name.Equals("label1"));

                StackPanel stackpan2 = chartgridimage.ChildrenOfType<StackPanel>().FirstOrDefault(e => Grid.GetRow(e) == row && Grid.GetColumn(e) == column && e.Name.Equals("stackpan2"));
                Telerik.Windows.Controls.Label label2 = stackpan2.ChildrenOfType<Telerik.Windows.Controls.Label>().FirstOrDefault(e => e.Name.Equals("label2"));

                axisdata1.Add(label1.Content.ToString());
                axisdata2.Add(label2.Content.ToString());
                if (row == 7)
                {
                    if (column == 0)
                        break;
                }
            }
        }
    }

Screen Shot


1
ExportCHARTtoBMP()被调用在哪里?请展示代码的这一部分。 - DrKoch
@DrKoch,它在私有void charttoimageprocess(List<string> axisdata1, List<string> axisdata2)函数中。 - Annadate Piyush
1个回答

1
另一种看待你的问题的方式是,“为什么它第一次会渲染呢?”看起来你在测量和排列方面做了一些奇怪的事情,这可能会在第一次成功后失败。如果我们在charttoimageprocess中内联ExportCHARTtoBMP,我们将看到以下代码片段:
chart.UpdateLayout();
chart.Arrange(new Rect(new System.Windows.Size(chart.ActualWidth, chart.ActualHeight)));
chart.UpdateLayout();
chartx.Measure(new System.Windows.Size(double.PositiveInfinity, double.PositiveInfinity)); // from charttoimageprocess(...)
int chartW = (int)Math.Round(chartx.ActualWidth);
int chartH = (int)Math.Round(chartx.ActualHeight);

这里的问题是:
  1. 除非您的代码是派生自UIElement对象并且您正在布局其子项(这与Grid所做的相冲突),否则不应从用户代码中调用ArrangeMeasure(通常还有UpdateLayout)。
  2. MeasureArrange(以及UpdateLayout)都会影响布局,但实际上并不会呈现结果布局更改。
第一个问题可能就是您的问题。如果您消除了上面的Arrange/Measure/UpdateLayout调用,您的代码可能会正常工作。
第二个问题可能也存在——你的代码可能有效,但会使图表的布局失效,它们没有时间重新渲染。在 WPF 中,呈现发生在与 UI 布局不同的线程上,并且随着它检测到布局更新并尝试匹配特定帧速率(通常为 30、60 或 120 fps)而运行。 (它暂停 UI 线程以运行,并且有时会在 UpdateLayout 期间运行)。要么第一个成功了,其他的还没有完成渲染就来不及显示,要么它使用缓存的位图图像,但其他图形继续被重新布局(因为当您重新测量/排列其子元素时,网格也会受到影响,从而导致它对其其他子元素执行相同的操作),导致每个后续图形尝试重新渲染更多次。通过删除上面代码中的 Arrange/Measure,也可以消除这种情况。
最后,您可以通过将整个Grid渲染为位图,然后根据简单的数学将该位图分成 6 个较小的位图来解决所有这些问题(因为它被分成了 3 个相等大小的列和 2 个相等大小的行)。

即使我正在检查相同的内容以消除它。但是否有办法将整个图表的图像切成每个图表作为图像? - Annadate Piyush
@AnnadatePiyush,您可以使用RenderTargetBitmap.CopyPixels来将图像的子部分复制到数组中。将整数数组转换为位图不应该太难 - BmpBitmapEncoderBitmapFrame可能有一个重载,允许您传递带有步幅/宽度的数组。 - NextInLine

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接