导出图表(从网格)到bmp

时间:2015-01-27 06:39:35

标签: c# wpf image xaml wpfdatagrid

我想将每个图表从网格导出到系统中的图像文件。 下面是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#CODE:

 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 个答案:

答案 0 :(得分:1)

另一种看待你的问题的方法是,&#34;为什么它第一次渲染?&#34;。看起来有些事情你做的很奇怪,有些措施和安排可能会在第一时间起作用,然后再次失败。

如果我们将ExportCHARTtoBMP内联到charttoimageprocess,我们会看到以下代码段:

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. ArrangeMeasure(通常是UpdateLayout),除非您的代码是UIElement派生的对象并且您正在铺设你的孩子(这与Grid正在做的事情相冲突)
  2. MeasureArrange(以及UpdateLayout)都会影响布局,但实际上渲染结果布局变化。
  3. 第一个问题可能是你的问题。如果您取消了上面的Arrange / Measure / UpdateLayout次调用,那么您的代码可能会正常运行。

    第二个问题也可能在起作用 - 您的代码可能正在运行但是图表的布局无效,而且他们没有时间重新渲染。 WPF中的渲染发生在与UI布局不同的线程上,并在检测到布局更新并尝试匹配特定帧速率(通常为30,60或120 fps)时运行。 (它暂停UI线程运行,有时可能在UpdateLayout期间运行)。要么第一个成功而另一个没有及时完成渲染显示,要么它使用缓存的位图图像,但其他人继续重新布局(因为当您重新测量时网格也会受到影响/安排其子项,这反过来使其对其他子项做同样的事情),导致每个后续图形尝试重新渲染越来越多次。通过从上面的代码中删除排列/测量,也可以消除这种情况。

    最后,您可以通过将整个Grid渲染到位图来绕过所有这些问题,然后根据简单的数学将该位图切割成6个较小的位图(因为它被分成3个相等大小的位图)列和2个相等大小的行)。