canvas to image - 해당되는 글 1건

Exporting canvas to PNG image

public void ExportToPng(Uri path, Canvas surface)
{
  if (path == null) return;

  // 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.Width, surface.Height);
  // 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);

  // Create a file stream for saving image
  using (FileStream outStream = new FileStream(path.LocalPath, FileMode.Create))
  {
    // Use png encoder for our data
    PngBitmapEncoder encoder = new PngBitmapEncoder();
    // push the rendered bitmap to it
    encoder.Frames.Add(BitmapFrame.Create(renderBitmap));
    // save the data to the stream
    encoder.Save(outStream);
  }

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

I’ve again tried to make the code self-explaining. You can try to use different encoders for saving the data so it’s up to you to look through what ones are available. The main trouble many guys come across is blank images as the canvas output or saving canvas programmatically without visualizing it. The key is measuring and arranging the surface before pushing it to render bitmap. As render bitmap does nothing with measuring and arranging elements in this case, this should be regarded mandatory for you. And of course you should remember the zooming/scaling and rotation matrices ;) the layout transformations will also be saved to the image so you should take care of that manually. In my case I cache the LatoutTransform and reset the original value right before measuring canvas.

Paying more attention you can see that bitmap encoder accepts a Visual so canvas is not the only element that can be passed ;) Going down the inheritance tree it is convenient to use the FrameworkElement or pure Visual. FrameworkElement contains Width and Height properties used by canvas.

Default set references provided by WPF application template is quite enough to implement this method. Of course you’ll have to resolve "using" section ;)

Exporting canvas to XPS document

Actually this was the first I’ve started implementing for my application. I like XPS format very much because it is easy to maintain and integrate. Here’s the quick snippet of exporting your canvas to XPS

public void Export(Uri path, Canvas surface)
{
  if (path == null) return;

  // Save current canvas transorm
  Transform transform = surface.LayoutTransform;
  // Temporarily reset the layout transform before saving
  surface.LayoutTransform = null;

  // Get the size of the canvas
  Size size = new Size(surface.Width, surface.Height);
  // Measure and arrange elements
  surface.Measure(size);
  surface.Arrange(new Rect(size));

  // Open new package
  Package package = Package.Open(path.LocalPath, FileMode.Create);
  // Create new xps document based on the package opened
  XpsDocument doc = new XpsDocument(package);
  // Create an instance of XpsDocumentWriter for the document
  XpsDocumentWriter writer = XpsDocument.CreateXpsDocumentWriter(doc);
  // Write the canvas (as Visual) to the document
  writer.Write(surface);
  // Close document
  doc.Close();
  // Close package
  package.Close();

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

You will need two additional assemblies to be referenced from the GAC: "ReachFramework.dll" and "System.Printing.dll". Again I’d advice you to backup the original layout transformation regardless it’s state before processing canvas. You should definitely read more details towards System.IO.Packaging and XPS documents. For those who doesn’t know yet the XPS document is presented by zip container for some xml, fonts and binary data like images etc. You can freely rename XPS to ZIP, unpack it and look inside for better understanding when you’ll be looking though MSDN articles towards it.

I was really pleased to find out that 10 or 20 of my duplicated elements having the same image with content template loaded from the separate assembly wasn’t cloned. I had only one image in the XPS for each distinct type of element so the archive size was extremely small for my diagram.

Exporting canvas to the XAML

I’ll give you the most stupid sample that can be found everywhere

public void Export(Uri path, Canvas surface)
    {
      if (path == null) return;
      if (surface == null) return;

      string xaml = XamlWriter.Save(surface);
      File.WriteAllText(path.LocalPath, xaml);
    }

You get the xaml presentation of your canvas that can be later loaded with XamlReader. Be prepared to cuss MS guys a lot because it is rather difficult to adopt any application load anything except simple xamls into your canvas :) The worst thing is that it doesn’t support bindings/multibindings so you’ll have to resolve them somehow on document loading. The same is for events and there’s a list of things not supported that can be easily found at MSDN shipped with Visual Studio. I’ve looked through the Internet to find any good solution but there’s nothing I liked. Guess if it becomes a hot topic (and I’m sure it will become soon) a lot of good overrides will appear. I’m doing my own implementation but it’s in the early stage and cannot be brought to public.


저작자 표시
신고

'Study > WPF' 카테고리의 다른 글

[WPF_Program: Window Desktop Interface]  (3) 2011.02.12
[WPF: insert XAML into RichTextBox]  (0) 2010.12.20
[WPF: Diagramming. Saving you canvas to image]  (0) 2010.10.20
[WPF : Data Binding]  (0) 2010.07.28
Trackback 0 | Comment 0

강군v's Blog is powered by Daum & tistory

 

티스토리 툴바