Saving a WPF canvas as an image

前端 未结 4 534
天命终不由人
天命终不由人 2020-12-16 00:34

I was following this article and I got my canvas to be saved, however, I want to extend the code\'s functionality and save a particular part of my canvas as an image, rather

相关标签:
4条回答
  • 2020-12-16 01:14

    A simple method would be to use a CroppedBitmap after rendering the whole canvas. You could reuse the same RenderTargetBitmap, if you need multiple images.

    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);
    }
    

    If you want to save to a bitmap object instead of a file, you can use:

    using (Stream s = new MemoryStream())
    {
        pngEncoder.Save(s);
        Bitmap myBitmap = new Bitmap(s);
    }
    
    0 讨论(0)
  • 2020-12-16 01:15

    Looking at the link you posted, obviously you can choose the rendered target coordinates here.

    RenderTargetBitmap rtb = new RenderTargetBitmap((int)rect.Right,
         (int)rect.Bottom, 96d, 96d, System.Windows.Media.PixelFormats.Default);
    
    0 讨论(0)
  • 2020-12-16 01:31

    I know this is an old question, but it took me a while of searching and trying different answers to come up with something that worked reliably well. So to save some time for those in the future, here is a little service to either save a canvas out to a file, or return an ImageSource for display elsewhere in your application.

    It should be made more robust for a production application, additional null and error checking, etc..

    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;
        }
    }
    

    And a sample WPF app demonstrating it's use.

    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>
    

    And the code behind making the calls into the service.

    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");
        }
    }
    
    0 讨论(0)
  • 2020-12-16 01:35

    See if this solution works for you.

    Size size = new Size(width, height);
    canvas.Measure(size);
    canvas.Arrange(new Rect(X, Y, width, height));
    
    //Save Image
    ...  
    ...
    
    // Revert old position
    canvas.Measure(new Size());
    
    0 讨论(0)
提交回复
热议问题