如何以编程方式将非常宽的 GridView 转换为位图图像?

How can I programmatically convert a very wide GridView to a bitmap image?

如何以编程方式将非常宽的 GridView 转换为位图图像?我需要所有数据显示在一张位图图像中。

Xaml:

<StackPanel>
    <telerik:RadGridView x:Name="GridView1" ItemsSource="{Binding MyProperties1}" AutoGenerateColumns="True" />
    <telerik:RadGridView x:Name="GridView2" ItemsSource="{Binding MyProperties2}" AutoGenerateColumns="True" />
</StackPanel>

在Windows表单中使用DrawToBitmap很方便,可以为所欲为。在 WPF 中,您可以使用 RenderTargetBitmapBitmapFrameBmpBitmapEncoder 来做到这一点。

    void SaveToBmp(FrameworkElement control, string fileName)
    {
        var bmpEncoder = new BmpBitmapEncoder();
        SaveControlToImage(control, fileName, bmpEncoder);
    }

    void SaveControlToImage(FrameworkElement control, string fileName, BitmapEncoder encoder)
    {
        // Keep the current postion relative to the container
        UIElement container = VisualTreeHelper.GetParent(control) as UIElement;
        Point curPos = control.TranslatePoint(new Point(0.0, 0.0), container);

        RenderTargetBitmap bitmap = new RenderTargetBitmap((int)control.ActualWidth, (int)control.ActualHeight, 96, 96, PixelFormats.Pbgra32);
        Size visualSize = new Size(control.ActualWidth, control.ActualHeight);
        control.Measure(visualSize);
        control.Arrange(new Rect(visualSize));
        bitmap.Render(control);
        BitmapFrame frame = BitmapFrame.Create(bitmap);
        encoder.Frames.Add(frame);

        using (var stream = File.Create(fileName))
        {
            encoder.Save(stream);
        }

        control.Arrange(new Rect(curPos, visualSize));
    }

并使用以下代码将您的 GridView 保存到图像文件:

SaveToBmp(GridView1, @"D:\gridview1.bmp");

参考Easiest way of saving wpf Image control to a file并添加一些改进。

@Yanger Yang's is close, but you should use the element's DesiredSize instead of its ActualWidth/ActualHeight。当然,这是假设您没有在 Grid 元素本身上设置明确的大小。这种方法应该适用于您的 telerik:RadGridView,但我没有可用于测试的库。


这是一个示例项目,其中包含 GridScrollViewer 内有十五 (15) 列。

<Window x:Class="PrintableGridDemo.MainWindow"
        Title="Printable Grid" Height="600" Width="800">
    <DockPanel>
        <Button DockPanel.Dock="Top" 
                HorizontalAlignment="Right" 
                Margin="0 10 10 0" 
                Padding="50 10"
                Content="Print"
                Click="PrintButton_Click"/>
        <Border Margin="10" 
                BorderBrush="DarkGray" 
                BorderThickness="1">
            <ScrollViewer HorizontalScrollBarVisibility="Visible"
                          VerticalScrollBarVisibility="Visible">
                <Grid x:Name="MyGrid" 
                      Background="White">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="100"/>
                        <ColumnDefinition Width="100"/>
                        <ColumnDefinition Width="100"/>
                        <ColumnDefinition Width="100"/>
                        <ColumnDefinition Width="100"/>
                        <ColumnDefinition Width="100"/>
                        <ColumnDefinition Width="100"/>
                        <ColumnDefinition Width="100"/>
                        <ColumnDefinition Width="100"/>
                        <ColumnDefinition Width="100"/>
                        <ColumnDefinition Width="100"/>
                        <ColumnDefinition Width="100"/>
                        <ColumnDefinition Width="100"/>
                        <ColumnDefinition Width="100"/>
                        <ColumnDefinition Width="100"/>
                    </Grid.ColumnDefinitions>
                </Grid>
            </ScrollViewer>
        </Border>
    </DockPanel>
</Window>

我已经 (ab) 使用 class 的构造函数用包含列号的示例数据填充 Grid,这样您就可以看到生成的图像包含网格.

public MainWindow()
{
    InitializeComponent();

    for (int col = 0; col < MyGrid.ColumnDefinitions.Count; col++)
    {
        var control = new TextBlock() { 
            Text = $"This is column number { col + 1 }. It is a column with almost no dynamic data. The only dynamic data is the number corresponding to this column's index in the grid plus one since users prefer one-indexed values while the framework uses zero-indexed values.",
            TextWrapping = TextWrapping.Wrap
        };
        MyGrid.Children.Add(control);
        Grid.SetColumn(control, col);
    }
}

这是项目 运行 完成时的样子。

这是单击“打印”按钮的结果。如您所见,使用网格的 DesiredSize 允许我们查看所有列的内容(并且还删除了网格扩展以填充 ScrollViewer 的额外布局空白)。


点击“打印”按钮后会发生以下情况:

private void PrintButton_Click(object sender, RoutedEventArgs e)
{
    // Set up the output file.
    string destination = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
    string fileName = $"GridImage_{DateTime.Now:yyyy-MMM-dd_hh-mm-ss-tt}.png";
    string filePath = Path.Combine(destination, fileName);
    
    // Render the control as a Bitmap.
    RenderTargetBitmap image = new((int)MyGrid.DesiredSize.Width, (int)MyGrid.DesiredSize.Height, 96, 96, PixelFormats.Pbgra32);
    image.Render(MyGrid);

    // Encode the Bitmap image as a PNG instead.
    PngBitmapEncoder encoder = new();
    encoder.Frames.Add(BitmapFrame.Create(image));

    // Save the PNG to the file.
    using FileStream stream = File.Create(filePath);
    encoder.Save(stream);
}

关键点在这里:

RenderTargetBitmap image = new(

    // The key bit.
    (int)MyGrid.DesiredSize.Width, 
    (int)MyGrid.DesiredSize.Height, 
    
    // Format specific parameters. You can ignore these.
    96, 96, PixelFormats.Pbgra32);

因为我们使用的是元素的 DesiredSize,渲染将包含通常不会渲染的元素部分(因为它们在 UI 中不可见)。

如果您 运行 遇到裁剪问题,您可以调用元素的 Measure 方法来重新计算元素的当前 DesiredSize.