WPF StackPanel PNG Захват не отображается правильно - PullRequest
0 голосов
/ 23 января 2019

Я пытаюсь создать png-снимок StackPanel, однако при сохранении я получаю искаженное представление, где все содержимое представляет собой черные прямоугольники, а размер неправильный. Ширина и высота в изображении сохраняются правильно, однако весь контент переносится наверх и сжимается вместе

<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 голосов
/ 25 января 2019

Все 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 для включения ActualWidth и ActualHeight.


ОБНОВЛЕНИЕ 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 (приложение ref test), ноне могу найти там ничего смешного.


Для полноты вот мое тестовое приложение.

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>
...