locked
Cannot print custom BitmapSource: InvalidOperationException RRS feed

  • Question

  • Since I need a BitmapSource that is able to serialize the image data itself to XAML, rather than a Url, I created a custom BitmapSource. The custom BitmapSource has a property called ImageData that can receive a Base64 string with the image data. In the setter, the image data is converter into an internal BitmapImage object and all methods/properties that need to be overridden call the corresponding method/property on the BitmapImage, as can be seen in the code below.

    The resulting custom BitmapSource displays fine ans serializes fine, but when I try to print it, I get an InvalidOperationException: "BitmapImage has not been initialized.  Call the BeginInit method, set the appropriate properties, and then call the EndInit method." I cannot find what I am doing wrong. Any help is appreciated.

    Below is the entire code of the custom BitmapSource. It contains two static methods to reproduce the problem. TestPrint1 tries to print an Image object with the custom BitmapSource as source, which causes the above error. TestPrint2 sets the internal BitmapImage as Image source and prints fine. How can I get TestPrint1 working as good as TestPrint2?

    using System;

    using System.Collections.Generic;

    using System.ComponentModel;

    using System.IO;

    using System.Linq;

    using System.Text;

    using System.Windows;

    using System.Windows.Controls;

    using System.Windows.Media;

    using System.Windows.Media.Imaging;

     

    namespace CedxExtensions

    {

        public class cedBitmapImage : BitmapSource

        {

            private BitmapImage m_objBitmapImage;

            private string m_strImageData;

     

            #region Constructor

     

            public cedBitmapImage()

            {

                m_objBitmapImage = new BitmapImage();

            }

     

            #endregion

     

            #region ImageData property

     

            [DefaultValue(null)]

            public string ImageData

            {

                set

                {

                    m_strImageData = value;

                    m_objBitmapImage = new BitmapImage();

     

                    if (value != null)

                    {

                        byte[] abytImageData = Convert.FromBase64String(value);

                        MemoryStream objStream = new MemoryStream(abytImageData);

     

                        m_objBitmapImage = new BitmapImage();

                        m_objBitmapImage.BeginInit();

                        m_objBitmapImage.StreamSource = objStream;

                        m_objBitmapImage.EndInit();

                    }

                }

                get

                {

                    return m_strImageData;

                }

            }

     

            #endregion

     

            #region Overridden properties

     

            public override PixelFormat Format { get { return m_objBitmapImage.Format; } }

            public override double DpiX { get { return m_objBitmapImage.DpiX; } }

            public override double DpiY { get { return m_objBitmapImage.DpiY; } }

            public override double Height { get { return m_objBitmapImage.Height; } }

            public override BitmapPalette Palette { get { return m_objBitmapImage.Palette; } }

            public override int PixelHeight { get { return m_objBitmapImage.PixelHeight; } }

            public override int PixelWidth { get { return m_objBitmapImage.PixelWidth; } }

            public override double Width { get { return m_objBitmapImage.Width; } }

     

            #endregion

     

            #region Overridden events

           

            public override event EventHandler<ExceptionEventArgs> DecodeFailed;

            public override event EventHandler DownloadCompleted;

            public override event EventHandler<ExceptionEventArgs> DownloadFailed;

            public override event EventHandler<DownloadProgressEventArgs> DownloadProgress;

     

            #endregion

     

            #region Overridden methods

     

            protected override Freezable CreateInstanceCore() { return new cedBitmapImage(); }

     

            public override void CopyPixels(Int32Rect sourceRect, Array pixels, int stride, int offset) { m_objBitmapImage.CopyPixels(sourceRect, pixels, stride, offset); }

            public override void CopyPixels(Array pixels, int stride, int offset) { m_objBitmapImage.CopyPixels(pixels, stride, offset); }

            public override void CopyPixels(Int32Rect sourceRect, IntPtr buffer, int bufferSize, int stride) { m_objBitmapImage.CopyPixels(sourceRect, buffer, bufferSize, stride); }

            public override bool IsDownloading { get { return m_objBitmapImage.IsDownloading; } }

            public override ImageMetadata Metadata { get { return m_objBitmapImage.Metadata; } }

            public override string ToString() { return m_objBitmapImage.ToString(); }

     

            #endregion

     

            public static void TestPrint1()

            {

                PrintDialog objPrintDialog = new PrintDialog();

     

                if (objPrintDialog.ShowDialog() == true)

                {

                    cedBitmapImage objImageSource = new cedBitmapImage();

                    Image objImage = new Image();

     

                    objImageSource.ImageData = "...";

                    objImage.Source = objImageSource;

                    objPrintDialog.PrintVisual(objImage, "TestPrint");

                }

            }

     

            public static void TestPrint2()

            {

                PrintDialog objPrintDialog = new PrintDialog();

     

                if (objPrintDialog.ShowDialog() == true)

                {

                    cedBitmapImage objImageSource = new cedBitmapImage();

                   

                    objImageSource.ImageData = "...";

                    objImageSource.PrintBitmapImage();

                }

            }

     

            public void PrintBitmapImage()

            {

                PrintDialog objPrintDialog = new PrintDialog();

     

                if (objPrintDialog.ShowDialog() == true)

                {

                    Image objImage = new Image();

     

                    objImage.Source = m_objBitmapImage;

                    objPrintDialog.PrintVisual(objImage, "TestPrint");

                }

            }

        }

    }

     

    P.S. I cannot include the image data as it makes the body too long. On demand, I can send the complete file with image data.

     


    Rutger Koperdraad.
    Tuesday, November 9, 2010 8:32 PM

Answers

  • Hi Yves and other forum users,

    The problem was actually casued by the default type converter of the cedBitmapImage object and could be solved by adding a custom type converter. To do so, four steps are needed:

    1. Define a custom type converter:

    using System;

    using System.ComponentModel;

     

    namespace CedxExtensions

    {

        class cedBitmapImageTypeConverter : TypeConverter

        {

            public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)

            {

                return base.CanConvertTo(context, destinationType);

            }

     

            public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)

            {

                return base.CanConvertFrom(context, sourceType);

            }

     

            public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture,

                                             object value, Type destinationType)

            {

                return base.ConvertTo(context, culture, value, destinationType);

            }

     

            public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture,

                                               object value)

            {

                return base.ConvertFrom(context, culture, value);

            }

        }

    }

     

    2. Define a custom type descriptor:

    using System;

    using System.ComponentModel;

     

    namespace CedxExtensions

    {

        class cedBitmapImageTypeDescriptor : CustomTypeDescriptor

        {

            public cedBitmapImageTypeDescriptor()

                : base()

            {

            }

     

            public cedBitmapImageTypeDescriptor(ICustomTypeDescriptor parent)

                : base(parent)

            {

            }

     

            public override TypeConverter GetConverter()

            {

                return new cedBitmapImageTypeConverter();

            }

        }

    }

     

    3. Define a custom type-description provider:

    using System;

    using System.ComponentModel;

     

    namespace CedxExtensions

    {

        class cedBitmapImageTypeDescriptionProvider : TypeDescriptionProvider

        {

            public cedBitmapImageTypeDescriptionProvider()

                : base()

            {

            }

     

            public cedBitmapImageTypeDescriptionProvider(TypeDescriptionProvider parent)

                : base(parent)

            {

            }

     

            public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance)

            {

                return new cedBitmapImageTypeDescriptor();

            }

        }

    }

     

    4. Add the type-description provider as an attribute to cedBitmapImage:

        [TypeDescriptionProvider(typeof(cedBitmapImageTypeDescriptionProvider))]

        public class cedBitmapImage : BitmapSource

     

    I don’t understand  exactly why this solves the problem not why the problem occurred, but it works. The type converter doesn’t do anything really.

    Regards,
    Rutger.


    Rutger Koperdraad.
    • Marked as answer by cucucucu Tuesday, December 7, 2010 12:08 AM
    Tuesday, December 7, 2010 12:07 AM

All replies

  • Hi Rutger Koperdraad,

    Image has a Source property of type System.Windows.Media.ImageSource. Using a type converter (System.Windows.Media.ImageSourceConverter), you can set the property to a simple string in XAML. Rather than leveraging the type converter to convert a simple string filename into an ImageSource, you can explicitly set Image’s Source property to any one of several ImageSource subclasses, for example, BitmapImage.I did some tests with a demo based on the code snippets you supplied. You may find it at http://cid-e52a44742984b88f.office.live.com/self.aspx/.Documents/Print%20image.zip.

     

    As far as I can see, you are intended to apply the internal field m_objBitmapImage to the Source property of Image Source. In TestPrint2 you are calling the internal method PrintBitmapImage like

    objImage.Source = m_objBitmapImage;
    

    But in TestPrint1 you are applying an instance of cedBitmapImage to Image Source property.

    objImage.Source = objImageSource;
    

    If you try to deserialize this image it will fail, the value of baseUri property is null. So if you go this way Image will lose necessary informations for PrintVisual to do the printing job.

    Hope it helps. If I misunderstand you please feel free to let me know.

     

    Best regards,

    Yves

     


    Please remember to click “Mark as Answer” on the post that helps you, and to click “Unmark as Answer” if a marked post does not actually answer your question. This can be beneficial to other community members reading the thread.
    Friday, November 12, 2010 3:12 AM
  • Hi Yves,

    Thanks for looking into my case. What happens is that the BitmapImage is not serializing correctly to XAML, because the baseUri is null and the image data gets lost in the serialization. But it prints fine, and if the print process involves some type of serialization, that apparently leaves the image data intact.

    The cedBitmapImage is serializing correctly, if you leave the ImageData property as defined in my code sample. The image data is contained in a string property that serializes as well as any other string property. But it doesn't print and it puzzles me where in the print process it is going wrong and what exactly is going wrong.

    What I need is an image object that serializes correctly to XAML and prints correctly.

    regards,
    Rutger.


    Rutger Koperdraad.
    Saturday, November 13, 2010 1:29 AM
  • Hi cucucucu,

    I spent some time on trying to repro your issue by leaving your ImageData property as defined. However when I tried to deserialize the image, it failed.

    I think this issue would be solved if you expose your m_objBitmapImage and then set objImage.Source with objImageSource.m_objBitmapImage; also you may create a property for it.

    As to the printing issue, we’d probably need to debug the problem in order to solve it. 

    Maybe you could share your code and image file to us or send it to my mailbox: v-yvesz at microsoft dot com.

     

    Best regards,

    Yves


    Please remember to click “Mark as Answer” on the post that helps you, and to click “Unmark as Answer” if a marked post does not actually answer your question. This can be beneficial to other community members reading the thread.
    Tuesday, November 16, 2010 8:01 AM
  • Hi Yves,

    I created a complete VS 2008 project. The test program shows an image object which has a cedBitmapImage object as source. If you click Save, the entire image object is serialized to a string. If you click Clear, the image object is removed from the window. If you click Open, the image object is recreated from the serialized string. If you click Print, it is printed.

    I get an InvalidOperationException if I try to print to the XPS Document Writer. I just discovered that it works fine on CutePDF. I have no real printer to test here. Maybe the problem is specific to the XPS Document Writer.

    I don't see how I can attach the project here, so I will mail it.

    Thanks a lot for your efforts. If my cedBitmapObject works as expected, I can make a big leap forward in my project.


    Rutger Koperdraad.
    Wednesday, November 17, 2010 9:15 AM
  • Hi Rutger,

    I spent some time on testing your demo. One thing is that if you print the composited image using a printer with your steps, the output is just fine. I support that maybe this is an issue related to XPS format. Since the serialized string only includes the CredBitmapImage object, some other properties may be nessecery for xps formatting. We'd probably need to debug the problem in order to solve it.

    In your case, you may take this workaround: expose your m_objBitmapImage as a property, and set the image source to it. Here is the demo I modified.

    http://cid-e52a44742984b88f.office.live.com/self.aspx/.Documents/BitmapImagePrintBug.zip

    You can also post the suggestions to our Connect feedback portal. Our developer will evaluate them seriously and take them into consideration when designing future release of the product.

    http://connect.microsoft.com/VisualStudio/

     

    If you still have any doubts or questions please feel free to let me know.

     

    Best regards,

    Yves


    Please remember to click “Mark as Answer” on the post that helps you, and to click “Unmark as Answer” if a marked post does not actually answer your question. This can be beneficial to other community members reading the thread.
    • Marked as answer by Yves.Z Wednesday, November 24, 2010 2:39 AM
    • Unmarked as answer by cucucucu Monday, December 6, 2010 11:56 PM
    Thursday, November 18, 2010 7:50 AM
  • Hi Yves,

    Thanks for your help. Your solution would work in specific cases, but I need a general runtime generated image source that serializes with the image data and prints for a general purpose designer application that stores it designs in XAML. The XPS document writer is often used for testing and it would be great if it works as expected. I submitted the issue on Microsoft Connect: https://connect.microsoft.com/VisualStudio/feedback/details/623999/cannot-print-custom-bitmapsource-invalidoperationexception. I hope they can supply a feasable workaround and solve the problem in the nest release.

    regards,
    Rutger.


    Rutger Koperdraad.
    Tuesday, November 23, 2010 4:43 PM
  • Hi cucucucu,

    Thank you for the valuable feedback!

    Yes, better solution should be with complete support for XPS document writer. That is what we would take into consideration in the next release.

    We are marking this issue as "Answered". If you have any new findings or concerns, please feel free to unmark the issue.

    Have a good day man!

     

    Best regards,

    Yves


    Please remember to click “Mark as Answer” on the post that helps you, and to click “Unmark as Answer” if a marked post does not actually answer your question. This can be beneficial to other community members reading the thread.
    Wednesday, November 24, 2010 2:38 AM
  • Hi Yves and other forum users,

    The problem was actually casued by the default type converter of the cedBitmapImage object and could be solved by adding a custom type converter. To do so, four steps are needed:

    1. Define a custom type converter:

    using System;

    using System.ComponentModel;

     

    namespace CedxExtensions

    {

        class cedBitmapImageTypeConverter : TypeConverter

        {

            public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)

            {

                return base.CanConvertTo(context, destinationType);

            }

     

            public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)

            {

                return base.CanConvertFrom(context, sourceType);

            }

     

            public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture,

                                             object value, Type destinationType)

            {

                return base.ConvertTo(context, culture, value, destinationType);

            }

     

            public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture,

                                               object value)

            {

                return base.ConvertFrom(context, culture, value);

            }

        }

    }

     

    2. Define a custom type descriptor:

    using System;

    using System.ComponentModel;

     

    namespace CedxExtensions

    {

        class cedBitmapImageTypeDescriptor : CustomTypeDescriptor

        {

            public cedBitmapImageTypeDescriptor()

                : base()

            {

            }

     

            public cedBitmapImageTypeDescriptor(ICustomTypeDescriptor parent)

                : base(parent)

            {

            }

     

            public override TypeConverter GetConverter()

            {

                return new cedBitmapImageTypeConverter();

            }

        }

    }

     

    3. Define a custom type-description provider:

    using System;

    using System.ComponentModel;

     

    namespace CedxExtensions

    {

        class cedBitmapImageTypeDescriptionProvider : TypeDescriptionProvider

        {

            public cedBitmapImageTypeDescriptionProvider()

                : base()

            {

            }

     

            public cedBitmapImageTypeDescriptionProvider(TypeDescriptionProvider parent)

                : base(parent)

            {

            }

     

            public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance)

            {

                return new cedBitmapImageTypeDescriptor();

            }

        }

    }

     

    4. Add the type-description provider as an attribute to cedBitmapImage:

        [TypeDescriptionProvider(typeof(cedBitmapImageTypeDescriptionProvider))]

        public class cedBitmapImage : BitmapSource

     

    I don’t understand  exactly why this solves the problem not why the problem occurred, but it works. The type converter doesn’t do anything really.

    Regards,
    Rutger.


    Rutger Koperdraad.
    • Marked as answer by cucucucu Tuesday, December 7, 2010 12:08 AM
    Tuesday, December 7, 2010 12:07 AM
  • I cried out my victory too early. The type converter solves the issue with the printing, but ruins the serialization. With the type converter attached, the ImageData property of the cedBitmapImage object is not written at all to XAML by the XamlWriter. So I'm still searching for a solution to this issue...
    Rutger Koperdraad.
    Tuesday, December 7, 2010 8:41 PM