WPF StackPanel PNG Capture无法正确呈现

时间:2019-01-23 15:41:21

标签: c# wpf stackpanel

我正在尝试创建StackPanel的png捕获,但是当我保存时,会得到一个扭曲的视图,其中所有内容均为黑色矩形,并且大小不正确。图片保存中的宽度和高度正确,但是所有内容都被压到顶部并压在一起

<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Views="clr-namespace:POExpress.Views" x:Class="POExpress.MainWindow"
Title="My Window" Height="500" MinWidth="1000" Width="1000">
<Grid>
    <TabControl>
        <TabItem Header="My Epics">
            <Grid Background="#FFE5E5E5">
                <Border Margin="0,52,0,0" BorderThickness="1" BorderBrush="Black">
                    <ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
                        <StackPanel x:Name="sp_ports" Orientation="Vertical"/>
                    </ScrollViewer>
                </Border>
                <Button x:Name="btn_capture" Content="Save to png" Margin="0,10,114,0" VerticalAlignment="Top" Height="31" Background="White" HorizontalAlignment="Right" Width="99" Click="Btn_capture_Click"/>
            </Grid>
        </TabItem>
    </TabControl>
</Grid>

public RenderTargetBitmap GetImage()
{
    Size size = new Size(sp_ports.ActualWidth, sp_ports.ActualHeight);
    if (size.IsEmpty)
        return null;

    RenderTargetBitmap result = new RenderTargetBitmap((int)size.Width, (int)size.Height, 96, 96, PixelFormats.Pbgra32);

    DrawingVisual drawingvisual = new DrawingVisual();
    using (DrawingContext context = drawingvisual.RenderOpen())
    {
        context.DrawRectangle(new VisualBrush(sp_ports), null, new Rect(new Point(), size));
        context.Close();
    }

    result.Render(drawingvisual);
    return result;
}

public static void SaveAsPng(RenderTargetBitmap src)
{
    Microsoft.Win32.SaveFileDialog dlg = new Microsoft.Win32.SaveFileDialog();
    dlg.Filter = "PNG Files | *.png";
    dlg.DefaultExt = "png";
    if (dlg.ShowDialog() == true)
    {
        PngBitmapEncoder encoder = new PngBitmapEncoder();
        encoder.Frames.Add(BitmapFrame.Create(src));
        using (var stream = dlg.OpenFile())
        {
            encoder.Save(stream);
        }
    }
}

private void Btn_capture_Click(object sender, RoutedEventArgs e)
{
    SaveAsPng(GetImage());
}

enter image description here

应呈现为什么(某些信息已涂黑) enter image description here

1 个答案:

答案 0 :(得分:6)

所有UIElements都继承自Visual,因此您可以将StackPanel直接提供给Render方法。

public RenderTargetBitmap GetImage()
{
    Size size = sp_ports.DesiredSize;
    if (size.IsEmpty)
        return null;

    RenderTargetBitmap result = new RenderTargetBitmap((int)size.Width, (int)size.Height, 96, 96, PixelFormats.Pbgra32);

    result.Render(sp_ports);
    return result;
}

更新

如@Clemens所指出的,直接使用UIElement有一些细微的复杂之处。然而,他的另一种评论是百万美元。

Size size = uiElement.DesiredSize

为我们提供uiElement可见部分的大小。

Size size = new Size(uiElement.ActualWidth, uiElement.ActualHeight)

返回uiElement的完整大小,并且也在不可见范围内扩展。

鉴于您遇到了这个问题,那么您就来解决这个问题。主要问题是渲染之前,您需要重新评估视觉效果。当前,您正在将整个视觉投影到UIElement的所需大小(可见部分)。

public RenderTargetBitmap GetImage(FrameworkElement element)
{
    Size size = new Size(element.ActualWidth, element.ActualHeight);
    if (size.IsEmpty)
        return null;
    element.Measure(size);
    element.Arrange(new Rect(size));

    RenderTargetBitmap result = new RenderTargetBitmap((int)size.Width, (int)size.Height, 96, 96, PixelFormats.Pbgra32);

    DrawingVisual drawingvisual = new DrawingVisual();
    using (DrawingContext context = drawingvisual.RenderOpen())
    {
        context.DrawRectangle(new VisualBrush(element), null, new Rect(new Point(), size));
    }

    result.Render(drawingvisual);
    return result;
}

我使用FrameworkElement合并了ActualWidthActualHeight


更新2

  

一旦我改变了堆叠面板的尺寸,屏幕截图就会再次消失。似乎记得最长的状态是什么,并以此压缩。

经过一番摆弄之后,我得以重现您的问题。当StackPanel必须延伸以填充任何剩余空间时,就会发生这种情况。解决方案是给uiElement无限的空间来计算其所需的大小,这使我们摆脱了对实际大小的依赖。

public RenderTargetBitmap GetImage(FrameworkElement element)
{
    element.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
    element.Arrange(new Rect(element.DesiredSize));

    Size size = element.DesiredSize;
    if (size.IsEmpty)
        return null;

    RenderTargetBitmap result = new RenderTargetBitmap((int)size.Width, (int)size.Height, 96, 96, PixelFormats.Pbgra32);

    DrawingVisual drawingvisual = new DrawingVisual();
    using (DrawingContext context = drawingvisual.RenderOpen())
    {
        context.DrawRectangle(new VisualBrush(element), null, new Rect(new Point(), size));
    }

    result.Render(drawingvisual);
    return result;
}

我已经检查了Expander的行为(参考测试应用程序),但找不到有趣的地方。


为完整起见,这是我的测试应用程序。

MainWindow.xaml.cs

using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;

namespace WpfApp
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        public RenderTargetBitmap GetImage(FrameworkElement element)
        {
            element.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
            element.Arrange(new Rect(element.DesiredSize));

            Size size = element.DesiredSize;
            if (size.IsEmpty)
                return null;

            RenderTargetBitmap result = new RenderTargetBitmap((int)size.Width, (int)size.Height, 96, 96, PixelFormats.Pbgra32);

            DrawingVisual drawingvisual = new DrawingVisual();
            using (DrawingContext context = drawingvisual.RenderOpen())
            {
                context.DrawRectangle(new VisualBrush(element), null, new Rect(new Point(), size));
            }

            result.Render(drawingvisual);
            return result;
        }

        public static void SaveAsPng(RenderTargetBitmap src)
        {
            Microsoft.Win32.SaveFileDialog dlg = new Microsoft.Win32.SaveFileDialog();
            dlg.Filter = "PNG Files | *.png";
            dlg.DefaultExt = "png";
            if (dlg.ShowDialog() == true)
            {
                PngBitmapEncoder encoder = new PngBitmapEncoder();
                encoder.Frames.Add(BitmapFrame.Create(src));
                using (var stream = dlg.OpenFile())
                {
                    encoder.Save(stream);
                }
            }
        }

        private void Btn_capture_Click(object sender, RoutedEventArgs e)
        {
            SaveAsPng(GetImage(sp_ports));
        }
    }
}

MainWindow.cs

<Window x:Class="WpfApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <DockPanel LastChildFill="True">
        <Button DockPanel.Dock="Top" Click="Btn_capture_Click">Take Pic</Button>
        <StackPanel x:Name="sp_ports">

            <DataGrid>
                <DataGrid.Columns>
                    <DataGridTextColumn Header="H1" Width="40"/>
                    <DataGridTextColumn Header="H2" Width="*"/>
                </DataGrid.Columns>
            </DataGrid>

            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition Height="200" />
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="400" />
                </Grid.RowDefinitions>
                <StackPanel Background="Red"/>
                <Expander Grid.Row="1" ExpandDirection="Down" IsExpanded="False">
                    <TabControl Height="400">
                        <TabItem Header="Tab 1">
                            <TextBox FontSize="50" TextWrapping="Wrap">Text for Tab 1</TextBox>
                        </TabItem>
                        <TabItem Header="Tab 2">
                            <TextBox FontSize="50" TextWrapping="Wrap">Text for Tab 1</TextBox>
                        </TabItem>
                    </TabControl>
                </Expander>
                <StackPanel Grid.Row="2" Background="Blue"/>
            </Grid>

            <DataGrid>
                <DataGrid.Columns>
                    <DataGridTextColumn Header="H1" Width="40"/>
                    <DataGridTextColumn Header="H2" Width="*"/>
                </DataGrid.Columns>
            </DataGrid>

        </StackPanel>
    </DockPanel>
</Window>