wpf - how to create and save any visual element into a file

Given a random Visual (type of System.Windows.Media.Visual) how to create a visual that represent the visual in memory and save that back to some file?

 

the key here are a few classes

 

System.Windows.Media.DrawingVisual

System.Windows.Media.DrawingImage

System.Windows.Media.Imaging.RenderTargetBitmap

System.Windows.Media.Imaging.BitmapFrame

 

 

 

and the flow of the classes as such

 

 

  1. you first create a DrawingVisual through the use of TreeHelper.GetDrawing(visual); while the return value of the call is DrawingGroup.
  2. you then can create an empty DrawingVisual, and open  it by DrawingVisual.RenderOpen to get a DrawingContent (System.Windows.Media.DrawingContext); so that you can draw the image to the drawing context.
  3. You can call the DrawingContext.DrawImage to draw the image onto the Drawing Context.
  4. After that, you can create a RenderTargetBitmap or any other type of Bitmap, initialize it withthe proper resultion settings and size (width and height)
  5. you then render the RenderTargetBitmap  with the Render(drawingVisual) call
  6. After all this, you can create a certain BitmapEncoder, in this case, you can use PngBitmapEncoder;
  7. Add a BitmapFrame to the BitmapEncoder, by add the bitmapFrame to the BitmapEncoder's Frames collection. To creat a BitmapFrame, you can use the static method of System.Windows.Media.Imaging.BitmapFrame.Create(RenderTargetBitmap bitmap);
  8. After all this, you can create a file stream , and call the BitmapEncoder.Save(stream) to save the image to file

 

Below is the code that demonstrate the whole process.

 

 

  private static void ExampleCreateImage(System.Windows.Media.Visual  visual, int width, int height) 
  {

    var drawingVisual = new DrawingVisual();
    var drawingImage = new DrawingImage(VisualTreeHelper.GetDrawing(visual));

    using (var dc = drawingVisual.RenderOpen())
    {
        var rectangle = new Rect
        {
          X = 0,
          Y = 0,
          Width = width,
          Height = height,
        };
        dc.DrawRectangle(Brushes.White, null, rectangle);
        dc.DrawImage(drawingImage, rectangle);
    }

    System.Windows.Media.Imaging.RenderTargetBitmap rtb = new System.Windows.Media.Imaging.RenderTargetBitmap(width,
      height, 96d, 96d, PixelFormats.Default);

    rtb.Render(drawingVisual);

    System.Windows.Media.Imaging.PngBitmapEncoder encoder = new System.Windows.Media.Imaging.PngBitmapEncoder();
    encoder.Frames.Add(System.Windows.Media.Imaging.BitmapFrame.Create(rtb));
    using (System.IO.FileStream stream = new System.IO.FileStream(Environment.ExpandEnvironmentVariables(@"%temp%\print\MyDrawingImage.png"), System.IO.FileMode.OpenOrCreate))
    {
      encoder.Save(stream);
      stream.Close();
    }
    // to use you can call as follow
    //var frameworkElement = new FrameworkElement();
    //ExampleCreateImage(frameworkElement, (int)frameworkElement.ActualWidth, (int)frameworkElement.ActualWidth);
  }

 

NOTE: only when the Visual is Actually System.Windows.Interop.HwndHost, then you will use the above technique.

 

If the Visual is a general Visual, other than System.Windows.Interop.HwndHost, you can use the VisualBrush.

 

Below the code show how you descriminate between HwndHost and other FrameworkElement, you can come up the Bitmap and serialization code which is missing in the previous example.

 

 

    // @return a instance of IPrintImage
    public static IPrintImage CreatePrintImage(object container_)
    {

      if (Content != null)
      {
        var hwndHost = Content as System.Windows.Interop.HwndHost;
        if (hwndHost != null)
        {
          var drawingVisual = CreateDrawingImage(hwndHost, hwndHost.ActualWidth, hwndHost.ActualHeight);
          return new PrintImage(hwndHost) { DrawingVisual = drawingVisual };
        }
        var content = Content as FrameworkElement;
        if (content != null)
        {
          var drawingVisual = CreateDrawingVisual(content, content.ActualWidth, content.ActualHeight);
          return new PrintImage(content) { DrawingVisual = drawingVisual };
        }
      }
      return null;
    }

    public static DrawingVisual CreateDrawingVisual(FrameworkElement visual, double width, double height)
    {
      var drawingVisual = new DrawingVisual();
      // open the Render of the DrawingVisual
      using (var dc = drawingVisual.RenderOpen())
      {
        var vb = new VisualBrush(visual) { Stretch = Stretch.None };
        var rectangle = new Rect
        {
          X = 0,
          Y = 0,
          Width = width,
          Height = height,
        };

        // draw the white background
        dc.DrawRectangle(Brushes.White, null, rectangle);
        // draw the visual
        dc.DrawRectangle(vb, null, rectangle);
      }
      return drawingVisual;
    }

    public static DrawingVisual CreateDrawingImage(Visual visual, double width, double height)
    {
      return CreateDrawingImage(new DrawingImage(VisualTreeHelper.GetDrawing(visual)), width, height);
    }

    public static DrawingVisual CreateDrawingImage(System.Windows.Media.DrawingImage drawingImage, double width, double height)
    {
      //var image = new System.Windows.Controls.Image();
      //image.Height = height;
      //image.Width = width;
      //image.Source = drawingImage;

      var drawingVisual = new DrawingVisual();
      // open the Render of the DrawingVisual
      using (var dc = drawingVisual.RenderOpen())
      {
        var rectangle = new Rect
        {
          X = 0,
          Y = 0,
          Width = width,
          Height = height,
        };

        // draw the white background
        dc.DrawRectangle(Brushes.White, null, rectangle);
        //NOTE:
        // instead of Creating one VisualBrush, it create and use one DrawingImage
        // which works with WebBrowser and get the right image back, while 
        // with the VisualBrush, it seems empty content is returned
        // 
        dc.DrawImage(drawingImage, rectangle);
      }

      return drawingVisual;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值