Silverlight 如何导出图片



在项目中我们经常会碰到需要将某些页面或者控件导出成图片的需求,例如将Chart导出.那么在Silverlight中,我们如何实现这样的功能呢.

  在老外的论坛上找到了解决方案,将控件转成流,然后在进行绘图导出.算法来自老外的论坛,稍微做了些调整.以下主要介绍使用方法.

  为了方便起见,我们建立一个文件夹存放要用到的三个类EditableImage.cs,PngEncoder.cs和ElementToPNG.cs,然后添加一个新页面,托入一个chart控件和一个button控件.

  

  在按钮事件中添加如下代码即可实现导出功能:

  

  
  
ElementToPNG eTP = new ElementToPNG(); eTP.ShowSaveDialog(myChart);

  点击按钮后弹出对话框如下:

  

  由于算法问题目前只能保存为png格式文件.

  小窍门:

  如果需要导出多个控件,可以将这些控件置于一个Canvas或者其他容器中,然后将该容器导出即可.

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.IO;

namespace  yxy
{
  public class EditableImage
  {
    private int                       _width=0;
    private int                       _height=0;
    private bool                      _init=false;
    private byte[]                    _buffer;
    private int                       _rowLength;

    public event EventHandler<EditableImageErrorEventArgs>    ImageError;
    
    public EditableImage(int width, int height)
    {
      this.Width = width;
      this.Height = height;
    }

    public int Width
    {
      get
      {
        return _width;
      }
      set
      {
        if (_init)
        {
          OnImageError("Error: Cannot change Width after the EditableImage has been initialized");
        }
        else if ((value <= 0) || (value > 2047))
        {
          OnImageError("Error: Width must be between 0 and 2047");
        }
        else
        {
          _width = value;
        }
      }
    }

    public int Height
    {
      get
      {
        return _height;
      }
      set
      {
        if (_init)
        {
          OnImageError("Error: Cannot change Height after the EditableImage has been initialized");
        }
        else if ((value <= 0) || (value > 2047))
        {
          OnImageError("Error: Height must be between 0 and 2047");
        }
        else
        {
          _height = value;
        }
      }
    }
    
    public void SetPixel(int col, int row, Color color)
    {
      SetPixel(col, row, color.R, color.G, color.B, color.A);
    }
    
    public void SetPixel(int col, int row, byte red, byte green, byte blue, byte alpha)
    {
      if (!_init)
      {
        _rowLength = _width*4 + 1;
        _buffer = new byte[_rowLength *_height];
 
        // Initialize
        for (int idx=0; idx<_height; idx++)
        {
          _buffer[idx*_rowLength] = 0;      // Filter bit
        }
 
        _init = true;
      }

      if ((col > _width) || (col < 0))
      {
        OnImageError("Error: Column must be greater than 0 and less than the Width");
      }
      else if ((row > _height) || (row < 0))
      {
        OnImageError("Error: Row must be greater than 0 and less than the Height");
      }
      
      // Set the pixel
      int   start = _rowLength*row + col*4 + 1;
      _buffer[start] = red;
      _buffer[start + 1] = green;
      _buffer[start + 2] = blue;
      _buffer[start + 3] = alpha;
    }
    
    public Color GetPixel(int col, int row)
    {
      if ((col > _width) || (col < 0))
      {
        OnImageError("Error: Column must be greater than 0 and less than the Width");
      }
      else if ((row > _height) || (row < 0))
      {
        OnImageError("Error: Row must be greater than 0 and less than the Height");
      }
      
      Color color = new Color();
      int   _base = _rowLength * row + col + 1;

      color.R = _buffer[_base];
      color.G = _buffer[_base + 1];
      color.B = _buffer[_base + 2];
      color.A = _buffer[_base + 3];
      
      return color;
    }
    
    public Stream GetStream()
    {
      Stream  stream;
 
      if (!_init)
      {
        OnImageError("Error: Image has not been initialized");
        stream = null;
      }
      else
      {
        stream = PngEncoder.Encode(_buffer, _width, _height);
      }
      
      return stream;
    }
 
    private void OnImageError(string msg)
    {
      if (null != ImageError)
      {
        EditableImageErrorEventArgs args = new EditableImageErrorEventArgs();
        args.ErrorMessage = msg;
        ImageError(this, args);
      }
    }
    
    public class EditableImageErrorEventArgs : EventArgs
    {
      private string    _errorMessage=string.Empty;
      
      public string ErrorMessage
      {
        get { return _errorMessage; }
        set { _errorMessage = value; }
      }
    }
    
  }
}

using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Media.Imaging;
using System.IO;

namespace yxy
{
    /// <summary>
    /// Based on:
    /// 
    /// Andy Beaulieu
    /// http://www.andybeaulieu.com/Home/tabid/67/EntryID/161/Default.aspx
    /// 
    /// 
    /// Tom Giam
    /// http://silverlight.net/forums/t/108713.aspx
    /// </summary>
    public class ElementToPNG
    {
        public void ShowSaveDialog(UIElement elementToExport)
        {

            //Canvas canvasToExport = e as Canvas;
            // Instantiate SaveFileDialog
            // and set defautl settings (just PNG export)

            SaveFileDialog sfd = new SaveFileDialog()
            {
                DefaultExt = "png",
                Filter = "Png files (*.png)|*.png|All files (*.*)|*.*",
                FilterIndex = 1,
                DefaultFileName = "海洋信息分析.png"
            };

            if (sfd.ShowDialog() == true)
            {
                SaveAsPNG(sfd, elementToExport);
            }
        }

        private void SaveAsPNG(SaveFileDialog sfd, UIElement elementToExport)
        {
            WriteableBitmap bitmap = new WriteableBitmap(elementToExport, new TranslateTransform());
            EditableImage imageData = new EditableImage(bitmap.PixelWidth, bitmap.PixelHeight);

            try
            {
                for (int y = 0; y < bitmap.PixelHeight; ++y)
                {

                    for (int x = 0; x < bitmap.PixelWidth; ++x)
                    {

                        int pixel = bitmap.Pixels[bitmap.PixelWidth * y + x];
                        imageData.SetPixel(x, y,

                        (byte)((pixel >> 16) & 0xFF),
                        (byte)((pixel >> 8) & 0xFF),

                        (byte)(pixel & 0xFF), (byte)((pixel >> 24) & 0xFF)
                        );

                    }

                }

            }
            catch (System.Security.SecurityException)
            {
                throw new Exception("Cannot print images from other domains");
            }

            // Save it to disk
            Stream pngStream = imageData.GetStream();

            //StreamReader sr = new StreamReader(pngStream);
            byte[] binaryData = new Byte[pngStream.Length];

            long bytesRead = pngStream.Read(binaryData, 0, (int)pngStream.Length);

            //using (Stream stream = sfd.OpenFile())
            //{

            //    stream.Write(binaryData, 0, binaryData.Length);

            //    stream.Close();

            //}

            Stream stream = sfd.OpenFile();

            stream.Write(binaryData, 0, binaryData.Length);

            stream.Close();


        }
    }
}




using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.IO;
using System.Windows.Browser;
using System.Reflection;

namespace yxy
{
  public class PngEncoder
  {
    private const int     _ADLER32_BASE = 65521;
    private const int     _MAXBLOCK = 0xFFFF;
    private static byte[] _HEADER = { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A };
    private static byte[] _IHDR = { (byte)'I', (byte)'H', (byte)'D', (byte)'R' };
    private static byte[] _GAMA = { (byte)'g', (byte)'A', (byte)'M', (byte)'A' };
    private static byte[] _IDAT = { (byte)'I', (byte)'D', (byte)'A', (byte)'T' };
    private static byte[] _IEND = { (byte)'I', (byte)'E', (byte)'N', (byte)'D' };
    private static byte[] _4BYTEDATA = { 0, 0, 0, 0 };
    private static byte[] _ARGB = { 0, 0, 0, 0, 0, 0, 0, 0, 8, 6, 0, 0, 0 };


    public static Stream Encode(byte[] data, int width, int height)
    {
      MemoryStream  ms = new MemoryStream();
      byte[]        size;
      
      // Write PNG header
      ms.Write(_HEADER, 0, _HEADER.Length);
      
      // Write IHDR
      //  Width:              4 bytes
      //  Height:             4 bytes
      //  Bit depth:          1 byte
      //  Color type:         1 byte
      //  Compression method: 1 byte
      //  Filter method:      1 byte
      //  Interlace method:   1 byte

      size = BitConverter.GetBytes(width);
      _ARGB[0] = size[3]; _ARGB[1] = size[2]; _ARGB[2] = size[1]; _ARGB[3] = size[0];
      
      size = BitConverter.GetBytes(height);
      _ARGB[4] = size[3]; _ARGB[5] = size[2]; _ARGB[6] = size[1]; _ARGB[7] = size[0];
      
      // Write IHDR chunk
      WriteChunk(ms, _IHDR, _ARGB);
      
      // Set gamma = 1
      size = BitConverter.GetBytes(1 * 100000);
      _4BYTEDATA[0] = size[3]; _4BYTEDATA[1] = size[2]; _4BYTEDATA[2] = size[1]; _4BYTEDATA[3] = size[0];
      
      // Write gAMA chunk
      WriteChunk(ms, _GAMA, _4BYTEDATA);
      
      // Write IDAT chunk
      uint    widthLength = (uint)(width*4)+1;
      uint    dcSize = widthLength * (uint)height;      
      
      // First part of ZLIB header is 78 1101 1010 (DA) 0000 00001 (01)
      // ZLIB info
      //
      // CMF Byte: 78
      //  CINFO = 7 (32K window size)
      //  CM = 8 = (deflate compression)
      // FLG Byte: DA
      //  FLEVEL = 3 (bits 6 and 7 - ignored but signifies max compression)
      //  FDICT = 0 (bit 5, 0 - no preset dictionary)
      //  FCHCK = 26 (bits 0-4 - ensure CMF*256+FLG / 31 has no remainder)
      // Compressed data
      //  FLAGS: 0 or 1
      //    00000 00 (no compression) X (X=1 for last block, 0=not the last block)
      //    LEN = length in bytes (equal to ((width*4)+1)*height
      //    NLEN = one's compliment of LEN
      //    Example: 1111 1011 1111 1111 (FB), 0000 0100 0000 0000 (40)
      //    Data for each line: 0 [RGBA] [RGBA] [RGBA] ...
      //    ADLER32

      uint          adler = ComputeAdler32(data);
      MemoryStream  comp = new MemoryStream();
      
      // Calculate number of 64K blocks
      uint          rowsPerBlock = _MAXBLOCK/widthLength;
      uint          blockSize = rowsPerBlock*widthLength;
      uint          blockCount;
      ushort        length;
      uint          remainder=dcSize;
      
      if ((dcSize % blockSize) == 0)
      {
        blockCount = dcSize/blockSize;
      }
      else
      {
        blockCount = (dcSize/blockSize)+1;
      }
      
      // Write headers
      comp.WriteByte(0x78);
      comp.WriteByte(0xDA);
      
      for (uint blocks=0; blocks<blockCount; blocks++)
      {
        // Write LEN
        length = (ushort)((remainder < blockSize) ? remainder : blockSize);
        
        if (length == remainder)
        {
          comp.WriteByte(0x01);
        }
        else
        {
          comp.WriteByte(0x00);
        }
        
        comp.Write(BitConverter.GetBytes(length), 0, 2);
        
        // Write one's compliment of LEN
        comp.Write(BitConverter.GetBytes((ushort)~length), 0, 2);
       
        // Write blocks
        comp.Write(data, (int)(blocks*blockSize), length);
        
        // Next block
        remainder -= blockSize;
      }
      
      WriteReversedBuffer(comp, BitConverter.GetBytes(adler));
      comp.Seek(0, SeekOrigin.Begin);
 
      byte[] dat = new byte[comp.Length];
      comp.Read(dat, 0, (int)comp.Length);
      
      WriteChunk(ms, _IDAT, dat);

      // Write IEND chunk
      WriteChunk(ms, _IEND, new byte[0]);

      // Reset stream
      ms.Seek(0, SeekOrigin.Begin);
           
      return ms;

      // See http://www.libpng.org/pub/png//spec/1.2/PNG-Chunks.html
      // See http://www.libpng.org/pub/png/book/chapter08.html#png.ch08.div.4
      // See http://www.gzip.org/zlib/rfc-zlib.html (ZLIB format)
      // See ftp://ftp.uu.net/pub/archiving/zip/doc/rfc1951.txt (ZLIB compression format)
    }
    
    private static void WriteReversedBuffer(Stream stream, byte[] data)
    {
      int     size = data.Length;
      byte[]  reorder = new byte[size];

      for (int idx=0; idx<size; idx++)
      {
        reorder[idx] = data[size-idx-1];
      }
      stream.Write(reorder, 0, size);
    }

    private static void WriteChunk(Stream stream, byte[] type, byte[] data)
    {
      int     idx;
      int     size=type.Length;
      byte[]  buffer = new byte[type.Length + data.Length];
      
      // Initialize buffer
      for (idx=0; idx<type.Length; idx++)
      {
        buffer[idx] = type[idx];
      }

      for (idx=0; idx<data.Length; idx++)
      {
        buffer[idx+size] = data[idx];
      }
      
      // Write length
      WriteReversedBuffer(stream, BitConverter.GetBytes(data.Length));
     
      // Write type and data
      stream.Write(buffer, 0, buffer.Length);   // Should always be 4 bytes
          
      // Compute and write the CRC
      WriteReversedBuffer(stream, BitConverter.GetBytes(GetCRC(buffer)));
    }
     
    private static uint[] _crcTable = new uint[256];
    private static bool   _crcTableComputed = false;

    private static void MakeCRCTable()
    {
      uint c;

      for (int n = 0; n < 256; n++)
      {
        c = (uint)n;
        for (int k = 0; k < 8; k++)
        {
          if ((c & (0x00000001)) > 0)
            c = 0xEDB88320 ^ (c >> 1);
          else
            c = c >> 1;
        }
        _crcTable[n] = c;
      }
  
      _crcTableComputed = true;
    }

    private static uint UpdateCRC(uint crc, byte[] buf, int len)
    {
      uint c = crc;

      if (!_crcTableComputed)
      {
        MakeCRCTable();
      }

      for (int n = 0; n < len; n++)
      {
        c = _crcTable[(c ^ buf[n]) & 0xFF] ^ (c >> 8);
      }

      return c;
    }

    /* Return the CRC of the bytes buf[0..len-1]. */
    private static uint GetCRC(byte[] buf)
    {
      return UpdateCRC(0xFFFFFFFF, buf, buf.Length) ^ 0xFFFFFFFF;
    }
        
    private static uint ComputeAdler32(byte[] buf)
    {
      uint  s1 = 1;
      uint  s2 = 0;
      int   length=buf.Length;

      for (int idx=0; idx<length; idx++)
      {
        s1 = (s1 + (uint)buf[idx]) % _ADLER32_BASE;
        s2 = (s2 + s1) % _ADLER32_BASE;
      }

      return (s2 << 16) + s1;
    }
  }
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值