2

I have a UserControl in a windows phone 8 app, where the user draws on this Usercontrol. I would like to convert this to an image, an example could be bitmap.

I have found this "converting a canvas into bitmap image in android", but I need it for Windows Phone 8.

The usercontrol is positioned in a canvas. The optimal would be if I only converted the usercontrol with information to an image. But If this cant be done then the canvas. If it has to be the Canvas, is it possible to set the background around the usercontrol to be invisible, since this information is not wanted.

EDIT Maybe this link can help somebody. How to render a WPF UserControl to a bitmap without creating a window

I will post my solution when it is done, I will also look into converting a bitmap back to usercontrol, if someone has looked into this please inform me about this.

EDIT 2

Has someone used this library http://writeablebitmapex.codeplex.com/ Should be pretty light weight and can see there is a function crop image. So maybe this is exactly what I need.

EDIT 3 So I have been looking more into this and finally found something that was exactly what I wanted, see http://www.kunal-chowdhury.com/2012/12/how-to-crop-image-based-on-shape-or-path.html

But I cannot seem to get this working. Has anyone an idea for this?

solution I use writeablebitmap to capture the ui element and i save it to isolatedstorage using mediastream. I can then reload it and use the image as imagesource and thereby crop the element to the wished shape.

Community
  • 1
  • 1
JTIM
  • 2,774
  • 1
  • 34
  • 74
  • It's probably possible to capture the contents of your usercontrol inside of a WriteableBitmap. From there, you can directly save it to JPEG using built-in APIs, or to another format by using the ImageTools library. – Kevin Gosse Sep 30 '13 at 15:52
  • So I have to set a property for the usercontrol for this to work or ? – JTIM Sep 30 '13 at 16:21

4 Answers4

3

This allows you to do it with pure WPF code using a RendeTargetBitmap, DrawingVisual and a VisualBrush.

Get System.Drawing.Bitmap of a WPF Area using VisualBrush

Posted as an answer because I had to do some hunting for a solution using visual brush and not a window.

Here is the the important code, horribly stolen:

public BitmapSource ConvertToBitmapSource(UIElement element)
{
    var target = new RenderTargetBitmap((int)(element.RenderSize.Width), (int)(element.RenderSize.Height), 96, 96, PixelFormats.Pbgra32);
    var brush = new VisualBrush(element);

    var visual = new DrawingVisual();
    var drawingContext = visual.RenderOpen();


    drawingContext.DrawRectangle(brush, null, new Rect(new Point(0, 0),
        new Point(element.RenderSize.Width, element.RenderSize.Height)));

    drawingContext.Close();

    target.Render(visual);

    return target;
}   

If you want to include some extra masking here, push an opacity mask while drawing.

drawingContext.PushOpacityMask(brush);

However, you should not have to as the same effect can be accomplished through XAML using the opacity mask property on your target element.

Here is my sample XAML

<Window x:Class="UIToBitmap.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <StackPanel>
        <Ellipse x:Name="circle" Height="100" Width="100" Fill="Blue"/>
        <Grid Background="Black">
            <Image x:Name="Image" Height="100"></Image>
        </Grid>
    </StackPanel>
</Window>

And the code behind:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace UIToBitmap
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            this.Loaded += MainWindow_Loaded;
        }

        void MainWindow_Loaded(object sender, RoutedEventArgs e)
        {
            var source = ConvertToBitmapSource(circle);
            Image.Source = source;
        }

        public BitmapSource ConvertToBitmapSource(UIElement element)
        {
            var target = new RenderTargetBitmap((int)(element.RenderSize.Width), (int)(element.RenderSize.Height), 96, 96, PixelFormats.Pbgra32);
            var brush = new VisualBrush(element);

            var visual = new DrawingVisual();
            var drawingContext = visual.RenderOpen();


            drawingContext.DrawRectangle(brush, null, new Rect(new Point(0, 0),
                new Point(element.RenderSize.Width, element.RenderSize.Height)));

            drawingContext.PushOpacityMask(brush);

            drawingContext.Close();

            target.Render(visual);

            return target;
        } 
    }
}

Note that the circle that is drawn on the black grid retains its opacity - only the circle is drawn.

Community
  • 1
  • 1
Gusdor
  • 14,001
  • 2
  • 52
  • 64
  • My question is, you convert the entire space, have you investigated that you are not interested in an square but a triangle. So with the left over area you wanted that to be see-through/invisible? – JTIM Oct 02 '13 at 07:56
  • @JTIM The question was to convert a UI Element (a canvas) to an image without opening a window. This succeeds. I'll edit in some extras. – Gusdor Oct 02 '13 at 08:19
2
public static class SBA
{       
    public static WriteableBitmap SaveAsWriteableBitmap(Canvas surface)
    {
        if (surface == null) return null;

        // Save current canvas transform
        Transform transform = surface.LayoutTransform;
        // reset current transform (in case it is scaled or rotated)
        surface.LayoutTransform = null;

        // Get the size of canvas
        Size size = new Size(surface.ActualWidth, surface.ActualHeight);
        // Measure and arrange the surface
        // VERY IMPORTANT
        surface.Measure(size);
        surface.Arrange(new Rect(size));

        // Get the size of canvas
        size = new Size(surface.ActualWidth, surface.ActualHeight);
        // Measure and arrange the surface
        // VERY IMPORTANT
        surface.Measure(size);
        surface.Arrange(new Rect(size));

        // Create a render bitmap and push the surface to it
        RenderTargetBitmap renderBitmap = new RenderTargetBitmap(
          (int)size.Width,
          (int)size.Height,
          96d,
          96d,
          PixelFormats.Pbgra32);
        renderBitmap.Render(surface);

        //Restore previously saved layout
        surface.LayoutTransform = transform;

        //create and return a new WriteableBitmap using the RenderTargetBitmap
        return new WriteableBitmap(renderBitmap);
    }

    public static BitmapImage WriteableBitmapToBitmapImage(WriteableBitmap wbm)
    {
        BitmapImage bmImage = new BitmapImage();
        using (MemoryStream stream = new MemoryStream())
        {
            PngBitmapEncoder encoder = new PngBitmapEncoder();
            encoder.Frames.Add(BitmapFrame.Create(wbm));
            encoder.Save(stream);
            bmImage.BeginInit();
            bmImage.CacheOption = BitmapCacheOption.OnLoad;
            bmImage.StreamSource = stream;
            bmImage.EndInit();
            bmImage.Freeze();
        }
        return bmImage;
    }

    public static BitmapImage CanvasToBitmap(Canvas c)
    {
        return WriteableBitmapToBitmapImage(SaveAsWriteableBitmap(c));
    }

}
Moumit
  • 8,314
  • 9
  • 55
  • 59
1

for your need there is third party library called WriteableBitmapEx that has lots of other feature that you may be in need in future.so just install this library from NugetPackageManager and then you can make a writeableBitmap of any control on the UI and then convert it to any image. i have a sample for it in which i have convert an button to an writeablebitmap and then save it to medialibrary(photos) in phone.here is the simple code but make sure you have install the WriteableBitmapEx.

here btn a Button defined in xaml..

private void btn_Click_1(object sender, RoutedEventArgs e)
    {

        BitmapImage img = new BitmapImage();

        imagebitmap = new WriteableBitmap(btn, null);

        imagebitmap.SaveToMediaLibrary("hello", false);

    }

you have to use this code directly. hope it helps you

loop
  • 9,002
  • 10
  • 40
  • 76
  • Thank you, and yes every help is appreciated. I will look into the library. But do you have any experience in making a non rectangle element a picture? If so what would happen with the areas that the control does not fill? – JTIM Sep 30 '13 at 16:23
  • JTIM i have checked it with applying it on a button with round borders and actually it only converting the area which control ocupying and if you thinking that you can make a image with dimentions of triangle then it is not possible because image will be always rectangle(i think so)..so just explore if it does not help you..if got something plz update here too – loop Sep 30 '13 at 16:33
  • Of course I will, I am about to explore this area with an update to my application. So wanted some information regarding best practices. And now I have a place to start. Just wanted to know If you had experience in that :) – JTIM Sep 30 '13 at 16:48
  • So now I finally have an app where this is needed, and I am trying to do as you said for demoing purposes. since what I can gather from reading the documentation of WriteableBitmapEx, it has exactly what I need. But I can not seem to get the code to work. I have installede it through nuget, but imagebitmap is giving an error. Furthermore do you now of a way to save the picture in isolatedstorage instead of in the apps pictures? – JTIM Nov 01 '13 at 14:10
-1

solution I use writeablebitmap to capture the ui element and i save it to isolatedstorage using mediastream. I can then reload it and use the image as imagesource and thereby crop the element to the wished shape.

JTIM
  • 2,774
  • 1
  • 34
  • 74