Skip navigation links
Home
Document Center
Blog
Videos
Forum
PhillyXAML.org > Blog > Posts > Extending FrameworkElement 4: Saving a Window or Control as an Image
Extending FrameworkElement 4: Saving a Window or Control as an Image

The final article in this series will expand on the previous article on how to save a window or control as an image. Since this is a pretty cool feature to have for screenshots and such, I decided to bake this functionality into the FrameworkElement class itself with a couple of cool overloads.

First we need to add a reference to System.Drawing, and some using statements as follows so it doesnt look like Christmas when we paste our code and everything is lit up in red!

using System.IO;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Microsoft.Win32;

Next, lets add our code to FrameworkElementExtender.cs. We have quite a few overrides here that basically correspond to the possible parameter combinations of resolution, output path and a flag for whether to fail gracefully or raise an exception in the event an error occurs.

        #region SaveImage Extension Methods
        /// <summary>
        /// Save this FrameworkElement as an image in a file specifying 
        /// X and Y resolution independently
        /// </summary>
        /// <param name="fe">The FrameworkElement being extended</param>
        /// <param name="JpegFilepath">Path to save the image file</param>
        /// <param name="DPIX">X Resolution in dots per inch</param>
        /// <param name="DPIY">Y Resolution in dots per inch</param>
        /// <param name="FailGracefully">True to return false when an Exception 
        /// is thrown; False to rethrow the inner Exception for handling from your code</param>
        /// <returns>Return true if save operation succeeds</returns>
        public static bool SaveImage(
            this FrameworkElement FE, 
            string JpegFilepath, 
            double DPIX, 
            double DPIY, 
            bool FailGracefully)
        {
            if (!JpegFilepath.EndsWith(".jpg"))
            {
                if (!FailGracefully)
                {
                    throw new Exception("Filename must end in .jpg");
                }
                else
                {
                    return false;
                }
            }
            try
            {
                int width = (int)FE.ActualWidth;
                int height = (int)FE.ActualHeight;
                VisualBrush vb = new VisualBrush(FE);
                Border bRender = new Border();
                ;

                bRender.Width = width;
                bRender.Height = height;
                bRender.Background = vb;

                RenderTargetBitmap rtb = new RenderTargetBitmap(
                      width,
                      height,
                      DPIX,
                      DPIY,
                      PixelFormats.Pbgra32);
                rtb.Render(bRender);

                JpegBitmapEncoder jbe = new JpegBitmapEncoder();
                jbe.QualityLevel = 100;
                jbe.Frames.Add(BitmapFrame.Create(rtb));
                MemoryStream ms = new MemoryStream();
                jbe.Save(ms);
                System.Drawing.Image proxy = System.Drawing.Image.FromStream(ms);
                proxy.Save(JpegFilepath);
                return true;
            }
            catch (Exception x)
            {
                if (!FailGracefully)
                {
                    throw x;
                }
                else
                {
                    return false;
                }
            }
        }
        /// <summary>
        /// Save this FrameworkElement as an image in a file specifying 
        /// X and Y resolution independently
        /// </summary>
        /// <param name="fe">The FrameworkElement being extended</param>
        /// <param name="JpegFilepath">Path to save the image file</param>
        /// <param name="DPIX">X Resolution in dots per inch</param>
        /// <param name="DPIY">Y Resolution in dots per inch</param>
        /// <returns>Return true if save operation succeeds</returns>
        public static bool SaveImage(
            this FrameworkElement FE, 
            string JpegFilepath, 
            double DPIX, 
            double DPIY)
        {
            return SaveImage(FE, JpegFilepath, DPIX, DPIY, true);
        }
        /// <summary>
        /// Save this FrameworkElement as an image in a file using the 
        /// same resolution for both and X and Y
        /// </summary>
        /// <param name="fe">The FrameworkElement being extended</param>
        /// <param name="JpegFilepath">Path to save the image file</param>
        /// <param name="DPIXY">X and Y Resolution in dots per inch</param>
        /// <returns>Return true if save operation succeeds</returns>
        public static bool SaveImage(
            this FrameworkElement FE, 
            string JpegFilepath, 
            double DPIXY)
        {
            return SaveImage(FE, JpegFilepath, DPIXY, DPIXY, true);
        }
        /// <summary>
        /// Save this FrameworkElement as an image in a file using the 
        /// same resolution for both and X and Y
        /// </summary>
        /// <param name="fe">The FrameworkElement being extended</param>
        /// <param name="JpegFilepath">Path to save the image file</param>
        /// <param name="DPIXY">X and Y Resolution in dots per inch</param>
        /// <param name="FailGracefully">True to return false when an Exception 
        /// is thrown; False to rethrow the inner Exception for handling from your code</param>
        /// <returns>Return true if save operation succeeds</returns>
        public static bool SaveImage(
            this FrameworkElement FE, 
            string JpegFilepath, 
            double DPIXY, 
            bool FailGracefully)
        {
            return SaveImage(FE, JpegFilepath, DPIXY, DPIXY, FailGracefully);
        }
        /// <summary>
        /// Save this FrameworkElement as an image in a file with DPIX and DPIY set to 96
        /// </summary>
        /// <param name="fe">The FrameworkElement being extended</param>
        /// <param name="JpegFilepath">Path to save the image file</param>
        /// <returns>Return true if save operation succeeds</returns>
        public static bool SaveImage(
            this FrameworkElement FE, 
            string JpegFilepath)
        {
            return SaveImage(FE, JpegFilepath, 96);
        }
        /// <summary>
        /// Save this FrameworkElement as an image in a file with DPIX and DPIY set to 96
        /// </summary>
        /// <param name="fe">The FrameworkElement being extended</param>
        /// <param name="JpegFilepath">Path to save the image file</param>
        /// <param name="FailGracefully">True to return false when an Exception 
        /// is thrown; False to rethrow the inner Exception for handling from your code</param>
        /// <returns>Return true if save operation succeeds</returns>
        public static bool SaveImage(
            this FrameworkElement FE, 
            string JpegFilepath, 
            bool FailGracefully)
        {
            return SaveImage(FE, JpegFilepath, 96, FailGracefully);
        }
        /// <summary>
        /// Save this FrameworkElement as an image in a file with DPIX and DPIY set to 96
        /// </summary>
        /// <param name="FE">The FrameworkElement being extended</param>
        /// <returns></returns>
        public static bool SaveImage(
            this FrameworkElement FE)
        {
            return SaveImage(FE, true);
        }
        /// <summary>
        /// Save this FrameworkElement as an image in a file with DPIX and DPIY set to 96
        /// </summary>
        /// <param name="FE">The FrameworkElement being extended</param>
        /// <param name="FailGracefully">True to return false when an Exception 
        /// is thrown; False to rethrow the inner Exception for handling from your code</param>
        /// <returns>Return true if save operation succeeds</returns>
        public static bool SaveImage(
            this FrameworkElement FE, 
            bool FailGracefully)
        {
            SaveFileDialog dlg = new SaveFileDialog();
            dlg.Filter = "JPEG (*.jpg)|*.jpg";
            if ((bool)dlg.ShowDialog())
            {
                return SaveImage(FE, dlg.FileName, FailGracefully);
            }
            return false;
        }
        #endregion

The first method override is our main worker method. The code is covered in the article mentioned above, so will not be covered here. The only additions are around correctly populating our parameters with our extended elements properties. Like the last article in this series, the remainder of the overrides provide recursive wrappers for passing default values eventually to this method.Since we are using this library in a XAML world, we will use 96 as our default dpi as per best practices, since XAML uses 96 Device Independent Units for displaying graphics. This allows for easy conversion.

Since there are a rather large number of overrides, that really only call one another, I will leave it up to you to come up with a way to test these.

Now that this series is complete, the final version of the source code will be available for download from the series landing page. Remember these extension methods apply to all classes that derive from FrameworkElement so you can easily provide screenshot capabilities to any control in your application without having to save the entire window. The parameterless form of the method isintended for just this purpose, and even provides a SaveFileDialog for you when called.

Comments

There are no comments yet for this post.