Speichern eines WPF-Canvas als Bild

 C Programming >> C-Programmierung >  >> Tags >> WPF
Speichern eines WPF-Canvas als Bild

Eine einfache Methode wäre die Verwendung eines CroppedBitmap nach dem Rendern der gesamten Leinwand. Sie könnten denselben RenderTargetBitmap wiederverwenden , wenn Sie mehrere Bilder benötigen.

RenderTargetBitmap rtb = new RenderTargetBitmap((int)canvas.RenderSize.Width,
    (int)canvas.RenderSize.Height, 96d, 96d, System.Windows.Media.PixelFormats.Default);
rtb.Render(canvas);

var crop = new CroppedBitmap(rtb, new Int32Rect(50, 50, 250, 250));

BitmapEncoder pngEncoder = new PngBitmapEncoder();
pngEncoder.Frames.Add(BitmapFrame.Create(crop));

using (var fs = System.IO.File.OpenWrite("logo.png"))
{
    pngEncoder.Save(fs);
}

Wenn Sie anstelle einer Datei in einem Bitmap-Objekt speichern möchten, können Sie Folgendes verwenden:

using (Stream s = new MemoryStream())
{
    pngEncoder.Save(s);
    Bitmap myBitmap = new Bitmap(s);
}

Ich weiß, dass dies eine alte Frage ist, aber ich brauchte eine Weile, um zu suchen und verschiedene Antworten auszuprobieren, um etwas zu finden, das zuverlässig gut funktionierte. Um also für die Zukunft etwas Zeit zu sparen, ist hier ein kleiner Dienst, um entweder eine Leinwand in einer Datei zu speichern oder eine ImageSource zur Anzeige an anderer Stelle in Ihrer Anwendung zurückzugeben.

Es sollte für eine Produktionsanwendung robuster gemacht werden, zusätzliche Null- und Fehlerprüfung usw.

public static class RenderVisualService
{
    private const double defaultDpi = 96.0;

    public static ImageSource RenderToPNGImageSource(Visual targetControl)
    {
        var renderTargetBitmap = GetRenderTargetBitmapFromControl(targetControl);

        var encoder = new PngBitmapEncoder();
        encoder.Frames.Add(BitmapFrame.Create(renderTargetBitmap));

        var result = new BitmapImage();

        using (var memoryStream = new MemoryStream())
        {
            encoder.Save(memoryStream);
            memoryStream.Seek(0, SeekOrigin.Begin);

            result.BeginInit();
            result.CacheOption = BitmapCacheOption.OnLoad;
            result.StreamSource = memoryStream;
            result.EndInit();
        }

        return result;
    }

    public static void RenderToPNGFile(Visual targetControl, string filename)
    {
        var renderTargetBitmap = GetRenderTargetBitmapFromControl(targetControl);

        var encoder = new PngBitmapEncoder();
        encoder.Frames.Add(BitmapFrame.Create(renderTargetBitmap));

        var result = new BitmapImage();

        try
        {
            using (var fileStream = new FileStream(filename, FileMode.Create))
            {
                encoder.Save(fileStream);
            }
        }
        catch (Exception ex)
        {
            System.Diagnostics.Debug.WriteLine($"There was an error saving the file: {ex.Message}");
        }
    }

    private static BitmapSource GetRenderTargetBitmapFromControl(Visual targetControl, double dpi = defaultDpi)
    {
        if (targetControl == null) return null;

        var bounds = VisualTreeHelper.GetDescendantBounds(targetControl);
        var renderTargetBitmap = new RenderTargetBitmap((int)(bounds.Width * dpi / 96.0),
                                                        (int)(bounds.Height * dpi / 96.0),
                                                        dpi,
                                                        dpi,
                                                        PixelFormats.Pbgra32);

        var drawingVisual = new DrawingVisual();

        using (var drawingContext = drawingVisual.RenderOpen())
        {
            var visualBrush = new VisualBrush(targetControl);
            drawingContext.DrawRectangle(visualBrush, null, new Rect(new Point(), bounds.Size));
        }

        renderTargetBitmap.Render(drawingVisual);
        return renderTargetBitmap;
    }
}

Und eine Beispiel-WPF-App, die ihre Verwendung demonstriert.

MainWindow.xaml

<Window x:Class="CanvasToBitmapDemo.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:CanvasToBitmapDemo"
    mc:Ignorable="d"
    Title="MainWindow" Height="450" Width="800">
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />
        <RowDefinition Height="Auto" />
        <RowDefinition Height="Auto" />
    </Grid.RowDefinitions>

    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="1*" />
        <ColumnDefinition Width="1*" />
    </Grid.ColumnDefinitions>

    <StackPanel Grid.Row="0" Grid.ColumnSpan="2" Orientation="Horizontal" HorizontalAlignment="Center">
        <Button Click="Button_Click" Content="Capture Image" Width="100"/>
        <Button Click="Button_Click_1" Content="Save To Disk" Width="100"/>
    </StackPanel>

    <Canvas x:Name="PART_Canvas" Grid.Row="1" Grid.Column="0">
        <Ellipse Canvas.Top="50"
                 Canvas.Left="60"
                 Fill="Gold"
                 Width="250"
                 Height="250" />

        <Polyline Stroke="#FF853D00"
                  StrokeThickness="10"
                  StrokeEndLineCap="Round"
                  StrokeStartLineCap="Round"
                  Points="110,100 120,97 130,95 140,94 150,95 160,97 170,100" />

        <Ellipse Canvas.Top="115"
                 Canvas.Left="114"
                 Fill="#FF853D00"
                 Width="45"
                 Height="50" />

        <Polyline Stroke="#FF853D00"
                  StrokeThickness="10"
                  StrokeEndLineCap="Round"
                  StrokeStartLineCap="Round"
                  Points="205,120 215,117 225,115 235,114 245,115 255,117 265,120" />

        <Ellipse Canvas.Top="120"
                 Canvas.Left="208"
                 Fill="#FF853D00"
                 Width="45"
                 Height="50" />

        <Polyline Stroke="#FF853D00"
                  StrokeThickness="10"
                  StrokeEndLineCap="Round"
                  StrokeStartLineCap="Round"
                  Points="150,220 160,216 170,215 180,215 190,216 202,218 215,221" />

    </Canvas>

    <Image x:Name="PART_Image" Grid.Row="1" Grid.Column="1" Stretch="None"/>
</Grid>

Und der Code hinter den Aufrufen des Dienstes.

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

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        PART_Image.Source = RenderVisualService.RenderToPNGImageSource(PART_Canvas);
    }

    private void Button_Click_1(object sender, RoutedEventArgs e)
    {
        RenderVisualService.RenderToPNGFile(PART_Canvas, "myawesomeimage.png");
    }
}