File: winforms\Managed\System\WinForms\PictureBox.cs
Project: ndp\fx\src\System.Windows.Forms.csproj (System.Windows.Forms)
//------------------------------------------------------------------------------
// <copyright file="PictureBox.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>                                                                
//------------------------------------------------------------------------------
 
namespace System.Windows.Forms {
    using System.Runtime.Serialization.Formatters;
    using System.Runtime.InteropServices;
    using System.Runtime.Versioning;
    using System.Diagnostics;
    using System.Diagnostics.CodeAnalysis;
 
    using System;
    using System.IO;
    using System.Security.Permissions;
    using System.Drawing;
    using System.Net;
    using System.ComponentModel;
    using System.ComponentModel.Design;
    using System.Threading;
    using System.Windows.Forms.Layout;
    using Microsoft.Win32;
 
    /// <include file='doc\PictureBox.uex' path='docs/doc[@for="PictureBox"]/*' />
    /// <devdoc>
    ///    <para> Displays an image that can be a graphic from a bitmap, 
    ///       icon, or metafile, as well as from
    ///       an enhanced metafile, JPEG, or GIF files.</para>
    /// </devdoc>
    [
    ComVisible(true),
    ClassInterface(ClassInterfaceType.AutoDispatch),
    DefaultProperty("Image"),
    DefaultBindingProperty("Image"),
    Docking(DockingBehavior.Ask),
    Designer("System.Windows.Forms.Design.PictureBoxDesigner, " + AssemblyRef.SystemDesign),
    SRDescription(SR.DescriptionPictureBox)
    ]
    public class PictureBox : Control, ISupportInitialize {
 
        /// <include file='doc\PictureBox.uex' path='docs/doc[@for="PictureBox.borderStyle"]/*' />
        /// <devdoc>
        ///     The type of border this control will have.
        /// </devdoc>
        private BorderStyle borderStyle = System.Windows.Forms.BorderStyle.None;
 
        /// <include file='doc\PictureBox.uex' path='docs/doc[@for="PictureBox.image"]/*' />
        /// <devdoc>
        ///     The image being displayed.
        /// </devdoc>
        private Image image;
 
        /// <include file='doc\PictureBox.uex' path='docs/doc[@for="PictureBox.sizeMode"]/*' />
        /// <devdoc>
        ///     Controls how the image is placed within our bounds, or how we are
        ///     sized to fit said image.
        /// </devdoc>
        private PictureBoxSizeMode sizeMode = PictureBoxSizeMode.Normal;
        private Size savedSize;
 
        bool currentlyAnimating;
 
        // Instance members for asynchronous behavior
        private AsyncOperation     currentAsyncLoadOperation = null;
        private string             imageLocation;
        private Image              initialImage;
        private Image              errorImage;
        private int                contentLength;
        private int                totalBytesRead;
        private MemoryStream       tempDownloadStream;
        private const int          readBlockSize = 4096;    
        private byte[]             readBuffer = null;
        private ImageInstallationType imageInstallationType;
        private SendOrPostCallback loadCompletedDelegate = null;
        private SendOrPostCallback loadProgressDelegate = null;
        private bool               handleValid = false;
        private object             internalSyncObject = new object();
 
        // These default images will be demand loaded.
        private Image              defaultInitialImage = null;
        private Image              defaultErrorImage = null;
 
        [ ThreadStatic ]
        private static Image       defaultInitialImageForThread = null;
        
        [ ThreadStatic ]
        private static Image       defaultErrorImageForThread = null;
 
 
        private static readonly object defaultInitialImageKey = new object();
        private static readonly object defaultErrorImageKey = new object();
        private static readonly object loadCompletedKey = new object();
        private static readonly object loadProgressChangedKey = new object();
        
 
        private const int   PICTUREBOXSTATE_asyncOperationInProgress    = 0x00000001;
        private const int   PICTUREBOXSTATE_cancellationPending         = 0x00000002;
        private const int   PICTUREBOXSTATE_useDefaultInitialImage      = 0x00000004;
        private const int   PICTUREBOXSTATE_useDefaultErrorImage        = 0x00000008;
        private const int   PICTUREBOXSTATE_waitOnLoad                  = 0x00000010;
        private const int   PICTUREBOXSTATE_needToLoadImageLocation     = 0x00000020;
        private const int   PICTUREBOXSTATE_inInitialization            = 0x00000040;
 
        // PERF: take all the bools and put them into a state variable
        private System.Collections.Specialized.BitVector32      pictureBoxState; // see PICTUREBOXSTATE_ consts above
 
        // http://msdn.microsoft.com/en-us/library/93z9ee4x(v=VS.100).aspx
        // if we load an image from a stream, 
        // we must keep the stream open for the lifetime of the Image
        StreamReader localImageStreamReader = null;
        Stream uriImageStream = null;
 
        /// <include file='doc\PictureBox.uex' path='docs/doc[@for="PictureBox.PictureBox"]/*' />
        /// <devdoc>
        ///    <para>Creates a new picture with all default properties and no 
        ///       Image. The default PictureBox.SizeMode will be PictureBoxSizeMode.NORMAL.
        ///    </para>
        /// </devdoc>
        public PictureBox() {
            // this class overrides GetPreferredSizeCore, let Control automatically cache the result
            SetState2(STATE2_USEPREFERREDSIZECACHE, true);  
 
            pictureBoxState = new System.Collections.Specialized.BitVector32(PICTUREBOXSTATE_useDefaultErrorImage |
                                                                             PICTUREBOXSTATE_useDefaultInitialImage);
 
            SetStyle(ControlStyles.Opaque |ControlStyles.Selectable , false);
            SetStyle(ControlStyles.OptimizedDoubleBuffer|ControlStyles.SupportsTransparentBackColor, true);
 
 
            TabStop = false;
            savedSize = Size;
        }
        
        /// <include file='doc\PictureBox.uex' path='docs/doc[@for="PictureBox.AllowDrop"]/*' />
        /// <internalonly/><hideinheritance/>
        /// <devdoc>
        /// </devdoc>
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
        public override bool AllowDrop {
            get {
                return base.AllowDrop;
            }
            set {
                base.AllowDrop = value;
            }
        }
 
        /// <include file='doc\PictureBox.uex' path='docs/doc[@for="PictureBox.BorderStyle"]/*' />
        /// <devdoc>
        ///    <para> Indicates the
        ///       border style for the control.</para>
        /// </devdoc>
        [
        DefaultValue(BorderStyle.None),
        SRCategory(SR.CatAppearance),
        DispId(NativeMethods.ActiveX.DISPID_BORDERSTYLE),
        SRDescription(SR.PictureBoxBorderStyleDescr)
        ]
        public BorderStyle BorderStyle {
            get {
                return borderStyle;
            }
 
            set {
                //valid values are 0x0 to 0x2
                if (!ClientUtils.IsEnumValid(value, (int)value, (int)BorderStyle.None, (int)BorderStyle.Fixed3D)){
                    throw new InvalidEnumArgumentException("value", (int)value, typeof(BorderStyle));
                }
 
                if (borderStyle != value) {
                    borderStyle = value;
                    RecreateHandle();
                    AdjustSize();
                }
            }
        }
 
        // Try to build a URI, but if that fails, that means it's a relative
        // path, and we treat it as relative to the working directory (which is
        // what GetFullPath uses).
        private Uri CalculateUri(string path)
        {
            Uri uri;
            try
            {
                uri = new Uri(path);
            }
            catch (UriFormatException)
            {
                // It's a relative pathname, get its full path as a file. 
                path = Path.GetFullPath(path);
                uri = new Uri(path);
            }
            return uri;
        }
 
        /// <include file='doc\PictureBox.uex' path='docs/doc[@for="PictureBox.CancelAsync"]/*' />
        [
        SRCategory(SR.CatAsynchronous),
        SRDescription(SR.PictureBoxCancelAsyncDescr)
        ]
        public void CancelAsync()
        {
            pictureBoxState[PICTUREBOXSTATE_cancellationPending] = true;
        }
 
        /// <include file='doc\PictureBox.uex' path='docs/doc[@for="PictureBox.CausesValidation"]/*' />
        /// <internalonly/>
        /// <devdoc/>
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
        public new bool CausesValidation {
            get {
                return base.CausesValidation;
            }
            set {
                base.CausesValidation = value;
            }
        }
        
        /// <include file='doc\PictureBox.uex' path='docs/doc[@for="PictureBox.CausesValidationChanged"]/*' />
        /// <internalonly/>
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
        new public event EventHandler CausesValidationChanged {
            add {
                base.CausesValidationChanged += value;
            }
            remove {
                base.CausesValidationChanged -= value;
            }
        }
 
        /// <include file='doc\PictureBox.uex' path='docs/doc[@for="PictureBox.CreateParams"]/*' />
        /// <internalonly/>
        /// <devdoc>
        ///    <para>Returns the parameters needed to create the handle. Inheriting classes
        ///       can override this to provide extra functionality. They should not,
        ///       however, forget to call base.getCreateParams() first to get the struct
        ///       filled up with the basic info.</para>
        /// </devdoc>
        protected override CreateParams CreateParams {
            [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)]
            get {
                CreateParams cp = base.CreateParams;
 
                switch (borderStyle) {
                    case BorderStyle.Fixed3D:
                        cp.ExStyle |= NativeMethods.WS_EX_CLIENTEDGE;
                        break;
                    case BorderStyle.FixedSingle:
                        cp.Style |= NativeMethods.WS_BORDER;
                        break;
                }
 
                return cp;
            }
        }
        
        /// <include file='doc\PictureBox.uex' path='docs/doc[@for="PictureBox.DefaultImeMode"]/*' />
        protected override ImeMode DefaultImeMode {
            get {
                return ImeMode.Disable;
            }
        }
 
        /// <include file='doc\PictureBox.uex' path='docs/doc[@for="PictureBox.DefaultSize"]/*' />
        /// <devdoc>
        ///     Deriving classes can override this to configure a default size for their control.
        ///     This is more efficient than setting the size in the control's constructor.
        /// </devdoc>
        protected override Size DefaultSize {
            get {
                return new Size(100, 50);
            }
        }
 
        /// <include file='doc\PictureBox.uex' path='docs/doc[@for="PictureBox.ErrorImage"]/*' />
        [
        SRCategory(SR.CatAsynchronous),
        Localizable(true),
        RefreshProperties(RefreshProperties.All),
        SRDescription(SR.PictureBoxErrorImageDescr)
        ]
        public Image ErrorImage {
            [ResourceExposure(ResourceScope.Machine)]
            [ResourceConsumption(ResourceScope.Machine)]
            get {
                // Strange pictureBoxState[PICTUREBOXSTATE_useDefaultErrorImage] approach used
                // here to avoid statically loading the default bitmaps from resources at
                // runtime when they're never used.
 
                if (errorImage == null && pictureBoxState[PICTUREBOXSTATE_useDefaultErrorImage])
                {
                    if (defaultErrorImage == null)
                    {
                        // Can't share images across threads.
                        if (defaultErrorImageForThread == null)
                        {
                            defaultErrorImageForThread =
                                new Bitmap(typeof(PictureBox),
                                           "ImageInError.bmp");
                        }
                        defaultErrorImage = defaultErrorImageForThread;
                    }
                    errorImage = defaultErrorImage;
                }
                return errorImage;
            }
            set {
                if (ErrorImage != value)
                {
                    pictureBoxState[PICTUREBOXSTATE_useDefaultErrorImage] = false;
 
                }
                errorImage = value;
            }
        }
 
        /// <include file='doc\PictureBox.uex' path='docs/doc[@for="PictureBox.ForeColor"]/*' />
        /// <internalonly/><hideinheritance/>
        /// <devdoc>
        /// </devdoc>
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
        public override Color ForeColor {
            get {
                return base.ForeColor;
            }
            set {
                base.ForeColor = value;
            }
        }
 
        /// <include file='doc\PictureBox.uex' path='docs/doc[@for="PictureBox.ForeColorChanged"]/*' />
        /// <internalonly/>
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
        new public event EventHandler ForeColorChanged {
            add {
                base.ForeColorChanged += value;
            }
            remove {
                base.ForeColorChanged -= value;
            }
        }
 
        /// <include file='doc\PictureBox.uex' path='docs/doc[@for="PictureBox.Font"]/*' />
        /// <internalonly/><hideinheritance/>
        /// <devdoc>
        /// </devdoc>
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
        public override Font Font {
            get {
                return base.Font;
            }
            set {
                base.Font = value;
            }
        }
 
        /// <include file='doc\PictureBox.uex' path='docs/doc[@for="PictureBox.FontChanged"]/*' />
        /// <internalonly/>
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
        new public event EventHandler FontChanged {
            add {
                base.FontChanged += value;
            }
            remove {
                base.FontChanged -= value;
            }
        }
 
        /// <include file='doc\PictureBox.uex' path='docs/doc[@for="PictureBox.Image"]/*' />
        /// <devdoc>
        /// <para>Retrieves the Image that the <see cref='System.Windows.Forms.PictureBox'/> is currently displaying.</para>
        /// </devdoc>
        [
        SRCategory(SR.CatAppearance),
        Localizable(true),
        Bindable(true),
        SRDescription(SR.PictureBoxImageDescr)
        ]
        public Image Image {
            [ResourceExposure(ResourceScope.Machine)]
            get {
                return image;
            }
            set {
                InstallNewImage(value, ImageInstallationType.DirectlySpecified);
            }
        }
 
        // The area occupied by the image
        /// <include file='doc\PictureBox.uex' path='docs/doc[@for="PictureBox.ImageLocation"]/*' />
        [
         SRCategory(SR.CatAsynchronous),
         Localizable(true),
         DefaultValue(null),
         RefreshProperties(RefreshProperties.All),
         SRDescription(SR.PictureBoxImageLocationDescr)
        ]
        public string ImageLocation 
        {
            get 
            {
                return imageLocation;
            }
            set 
            {
                // Reload even if value hasn't changed, since Image itself may
                // have changed.
                imageLocation = value;
 
                pictureBoxState[PICTUREBOXSTATE_needToLoadImageLocation] = !string.IsNullOrEmpty(imageLocation);
                
                // Reset main image if it hasn't been directly specified. 
                if (string.IsNullOrEmpty(imageLocation) &&
                    imageInstallationType != ImageInstallationType.DirectlySpecified)
                {
                    InstallNewImage(null, ImageInstallationType.DirectlySpecified);
                }
 
                if (WaitOnLoad && !pictureBoxState[PICTUREBOXSTATE_inInitialization] && !string.IsNullOrEmpty(imageLocation))
                {
                    // Load immediately, so any error will occur synchronously
                    Load();
                }
                
                Invalidate();
            }
        }
 
        private Rectangle ImageRectangle {
            get {
                return ImageRectangleFromSizeMode(sizeMode);
            }
        }
        
        private Rectangle ImageRectangleFromSizeMode(PictureBoxSizeMode mode)
        {
            Rectangle result = LayoutUtils.DeflateRect(ClientRectangle, Padding);
 
            if (image != null) {
                switch (mode) {
                  case PictureBoxSizeMode.Normal:
                  case PictureBoxSizeMode.AutoSize:
                      // Use image's size rather than client size.
                      result.Size = image.Size;
                      break;
 
                  case PictureBoxSizeMode.StretchImage:
                      // Do nothing, was initialized to the available dimensions.
                      break;
 
                  case PictureBoxSizeMode.CenterImage:
                      // Center within the available space.
                      result.X += (result.Width - image.Width) / 2;
                      result.Y += (result.Height - image.Height) / 2;
                      result.Size = image.Size;
                      break;
                  
                  case PictureBoxSizeMode.Zoom:
                    Size imageSize = image.Size;
                    float ratio = Math.Min((float)ClientRectangle.Width / (float)imageSize.Width, (float)ClientRectangle.Height / (float)imageSize.Height);
                    result.Width = (int)(imageSize.Width * ratio);
                    result.Height = (int) (imageSize.Height * ratio);
                    result.X = (ClientRectangle.Width - result.Width) /2;
                    result.Y = (ClientRectangle.Height - result.Height) /2;
                    break;
 
                  default:
                      Debug.Fail("Unsupported PictureBoxSizeMode value: " + mode);
                      break;
                }
            }
 
            return result;
        }
        
        /// <include file='doc\PictureBox.uex' path='docs/doc[@for="PictureBox.InitialImage"]/*' />
        [
        SRCategory(SR.CatAsynchronous),
        Localizable(true),
        RefreshProperties(RefreshProperties.All),
        SRDescription(SR.PictureBoxInitialImageDescr)
        ]
        public Image InitialImage {
            [ResourceExposure(ResourceScope.Machine)]
            [ResourceConsumption(ResourceScope.Machine)]
            get {
                // Strange pictureBoxState[PICTUREBOXSTATE_useDefaultInitialImage] approach
                // used here to avoid statically loading the default bitmaps from resources at
                // runtime when they're never used.
                
                if (initialImage == null && pictureBoxState[PICTUREBOXSTATE_useDefaultInitialImage])
                {
                    if (defaultInitialImage == null)
                    {
                        // Can't share images across threads.
                        if (defaultInitialImageForThread == null)
                        {
                            defaultInitialImageForThread =
                                new Bitmap(typeof(PictureBox),
                                           "PictureBox.Loading.bmp");
                        }
                        defaultInitialImage = defaultInitialImageForThread;
                    }
                    initialImage = defaultInitialImage;
                }
                return initialImage;
            }
            set {
                if (InitialImage != value)
                {
                    pictureBoxState[PICTUREBOXSTATE_useDefaultInitialImage] = false;
                }
                initialImage = value;
            }
        }
        
        private void InstallNewImage(Image value,
                                     ImageInstallationType installationType)
        {
            StopAnimate();
            this.image = value;
            
            LayoutTransaction.DoLayoutIf(AutoSize, this, this, PropertyNames.Image); 
            
            Animate();
            if (installationType != ImageInstallationType.ErrorOrInitial)
            {
                AdjustSize();
            }
            this.imageInstallationType = installationType;
            
            Invalidate();
            CommonProperties.xClearPreferredSizeCache(this);
        }
 
        /// <include file='doc\PictureBox.uex' path='docs/doc[@for="PictureBox.ImeMode"]/*' />
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
        new public ImeMode ImeMode {
            get {
                return base.ImeMode;
            }
            set {
                base.ImeMode = value;
            }
        }
 
        /// <include file='doc\PictureBox.uex' path='docs/doc[@for="PictureBox.ImeModeChanged"]/*' />
        /// <internalonly/>
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
        public new event EventHandler ImeModeChanged {
            add {
                base.ImeModeChanged += value;
            }
            remove {
                base.ImeModeChanged -= value;
            }
        }
 
        /// <include file='doc\PictureBox.uex' path='docs/doc[@for="PictureBox.Load"]/*' />
        // Synchronous load
        [
        SRCategory(SR.CatAsynchronous),
        SRDescription(SR.PictureBoxLoad0Descr)
        ]
        public void Load()
        {
            if (imageLocation == null || imageLocation.Length == 0)
            {
                throw new
                    InvalidOperationException(SR.GetString(SR.PictureBoxNoImageLocation));
            }
 
            // If the load and install fails, pictureBoxState[PICTUREBOXSTATE_needToLoadImageLocation] will be
            // false to prevent subsequent attempts.
            pictureBoxState[PICTUREBOXSTATE_needToLoadImageLocation] = false;
 
            Image img;
            ImageInstallationType installType = ImageInstallationType.FromUrl;
            try
            {
                DisposeImageStream();
 
                Uri uri = CalculateUri(imageLocation);
                if (uri.IsFile)
                {
                    localImageStreamReader = new StreamReader(uri.LocalPath);
                    img = Image.FromStream(localImageStreamReader.BaseStream);
                }
                else
                {
                    using (WebClient wc = new WebClient())
                    {
                        uriImageStream = wc.OpenRead(uri.ToString());
                        img = Image.FromStream(uriImageStream);
                    }
                }
            }
            catch
            {
                if (!DesignMode)
                {
                    throw;
                }
                else
                {
                    // In design mode, just replace with Error bitmap.
                    img = ErrorImage;
                    installType = ImageInstallationType.ErrorOrInitial;
                }
            }
 
            InstallNewImage(img, installType);
        }
 
        /// <include file='doc\PictureBox.uex' path='docs/doc[@for="PictureBox.Load2"]/*' />
        [
        SRCategory(SR.CatAsynchronous),
        SRDescription(SR.PictureBoxLoad1Descr),
        SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings") // PM review done
        ]
        public void Load(String url)
        {
            this.ImageLocation = url;
            this.Load();
        }
 
        /// <include file='doc\PictureBox.uex' path='docs/doc[@for="PictureBox.LoadAsync"]/*' />
        [
        SRCategory(SR.CatAsynchronous),
        SRDescription(SR.PictureBoxLoadAsync0Descr)
        ]
        public void LoadAsync()
        {
            if (imageLocation == null || imageLocation.Length == 0)
            {
                throw new
                    InvalidOperationException(SR.GetString(SR.PictureBoxNoImageLocation));
            }
 
            if (pictureBoxState[PICTUREBOXSTATE_asyncOperationInProgress])
            {
                //We shouldn't throw here: just return (VSWhidbey 160308).
                return;
            }
 
            pictureBoxState[PICTUREBOXSTATE_asyncOperationInProgress] = true;
            
            if ((Image == null ||
                 (imageInstallationType == ImageInstallationType.ErrorOrInitial))
                && InitialImage != null)
            {
                InstallNewImage(InitialImage, ImageInstallationType.ErrorOrInitial);
            }
 
            currentAsyncLoadOperation = AsyncOperationManager.CreateOperation(null);
            
            if (loadCompletedDelegate == null)
            {
                loadCompletedDelegate = new SendOrPostCallback(LoadCompletedDelegate);
                loadProgressDelegate = new SendOrPostCallback(LoadProgressDelegate);
                readBuffer = new byte[readBlockSize];
            }
            
            pictureBoxState[PICTUREBOXSTATE_needToLoadImageLocation] = false;
            pictureBoxState[PICTUREBOXSTATE_cancellationPending] = false;
            contentLength = -1;
            tempDownloadStream = new MemoryStream();
                        
            WebRequest req = WebRequest.Create(CalculateUri(imageLocation));
 
            // Invoke BeginGetResponse on a threadpool thread, as it has
            // unpredictable latency, since, on first call, it may load in the
            // configuration system (this is NCL bug 20605)
            (new WaitCallback(BeginGetResponseDelegate)).BeginInvoke(req, null, null);
        }
 
        // Solely for calling BeginGetResponse itself asynchronously.
        private void BeginGetResponseDelegate(object arg)
        {
            WebRequest req = (WebRequest)arg;
            req.BeginGetResponse(new AsyncCallback(GetResponseCallback), req);
        }
 
        private void PostCompleted(Exception error, bool cancelled)
        {
            AsyncOperation temp = currentAsyncLoadOperation;
            currentAsyncLoadOperation = null;
            if (temp != null)
            {
                temp.PostOperationCompleted(
                    loadCompletedDelegate,
                    new AsyncCompletedEventArgs(error, cancelled, null));
            }
        }
 
        private void LoadCompletedDelegate(object arg) 
        {
            AsyncCompletedEventArgs e = (AsyncCompletedEventArgs)arg;
 
            Image                 img         = ErrorImage;
            ImageInstallationType installType = ImageInstallationType.ErrorOrInitial;
 
            if (!e.Cancelled && e.Error == null)
            {
                // successful completion
                try
                {
                    img = Image.FromStream(tempDownloadStream);
                    installType = ImageInstallationType.FromUrl;
                }
                catch (Exception error)
                {
                    e = new AsyncCompletedEventArgs(error, false, null);
                }
            }
 
            // If cancelled, don't change the image
            if (!e.Cancelled)
            {
                InstallNewImage(img, installType);
            }
            
            tempDownloadStream = null;
            pictureBoxState[PICTUREBOXSTATE_cancellationPending] = false;
            pictureBoxState[PICTUREBOXSTATE_asyncOperationInProgress] = false;
            OnLoadCompleted(e);
        }
 
        private void LoadProgressDelegate(object arg)
        {
            OnLoadProgressChanged((ProgressChangedEventArgs)arg);
        }
        
        private void GetResponseCallback(IAsyncResult result) 
        {
            if (pictureBoxState[PICTUREBOXSTATE_cancellationPending])
            {
                PostCompleted(null, true);
            }
            else
            {
                try
                {
                    WebRequest req = (WebRequest)result.AsyncState;
                    WebResponse webResponse = req.EndGetResponse(result);
 
                    contentLength = (int)webResponse.ContentLength;
                    totalBytesRead = 0;
 
                    Stream responseStream = webResponse.GetResponseStream();
 
                    // Continue on with asynchronous reading.
                    responseStream.BeginRead(
                        readBuffer,
                        0,
                        readBlockSize,
                        new AsyncCallback(ReadCallBack),
                        responseStream);
 
                }
                catch (Exception error)
                {
                    // Since this is on a non-UI thread, we catch any exceptions and
                    // pass them back as data to the UI-thread.
                    PostCompleted(error, false);
                }
            }
        }
 
        private void ReadCallBack(IAsyncResult result) 
        {
            if (pictureBoxState[PICTUREBOXSTATE_cancellationPending])
            {
                PostCompleted(null, true);
            }
            else
            {
                Stream responseStream = (Stream)result.AsyncState;
                try
                {
                    int bytesRead = responseStream.EndRead(result);
 
                    if (bytesRead > 0)
                    {
                        totalBytesRead += bytesRead;
                        tempDownloadStream.Write(readBuffer, 0, bytesRead);
 
                        responseStream.BeginRead(
                            readBuffer,
                            0,
                            readBlockSize,
                            new AsyncCallback(ReadCallBack),
                            responseStream);
 
                        // Report progress thus far, but only if we know total length.
                        if (contentLength != -1)
                        {
                            int progress = (int)(100 * (((float)totalBytesRead) / ((float)contentLength)));
                            if (currentAsyncLoadOperation != null)
                            {
                                currentAsyncLoadOperation.Post(loadProgressDelegate,
                                       new ProgressChangedEventArgs(progress, null));
                            }
                        }
                    }
                    else
                    {
                        tempDownloadStream.Seek(0, SeekOrigin.Begin);
                        if (currentAsyncLoadOperation != null)
                        {
                            currentAsyncLoadOperation.Post(loadProgressDelegate,
                                       new ProgressChangedEventArgs(100, null));
                        }
                        PostCompleted(null, false);
 
                        // Do this so any exception that Close() throws will be
                        // dealt with ok.
                        Stream rs = responseStream;
                        responseStream = null;
                        rs.Close();
                    }
                }
                catch (Exception error)
                {
                    // Since this is on a non-UI thread, we catch any exceptions and
                    // pass them back as data to the UI-thread.
                    PostCompleted(error, false);
 
                    if (responseStream != null)
                    {
                        responseStream.Close();
                    }
                }
            }
        }
        
        
        /// <include file='doc\PictureBox.uex' path='docs/doc[@for="PictureBox.LoadAsync2"]/*' />
        [
        SRCategory(SR.CatAsynchronous),
        SRDescription(SR.PictureBoxLoadAsync1Descr),
        SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings") // PM review done
        ]
        public void LoadAsync(String url)
        {
            this.ImageLocation = url;
            this.LoadAsync();
        }
 
        /// <include file='doc\PictureBox.uex' path='docs/doc[@for="PictureBox.LoadCompleted"]/*' />
        [
        SRCategory(SR.CatAsynchronous),
        SRDescription(SR.PictureBoxLoadCompletedDescr)
        ]
        public event AsyncCompletedEventHandler LoadCompleted
        {
            add
            {
                this.Events.AddHandler(loadCompletedKey, value);
            }
            remove
            {
                this.Events.RemoveHandler(loadCompletedKey, value);
            }
        }
 
        /// <include file='doc\PictureBox.uex' path='docs/doc[@for="PictureBox.LoadProgressChanged"]/*' />
        [
        SRCategory(SR.CatAsynchronous),
        SRDescription(SR.PictureBoxLoadProgressChangedDescr)
        ]
        public event ProgressChangedEventHandler LoadProgressChanged
        {
            add
            {
                this.Events.AddHandler(loadProgressChangedKey, value);
            }
            remove
            {
                this.Events.RemoveHandler(loadProgressChangedKey, value);
            }
        }
        
        private void ResetInitialImage()
        {
            pictureBoxState[PICTUREBOXSTATE_useDefaultInitialImage] = true;
            initialImage = defaultInitialImage;
        }
        
        private void ResetErrorImage()
        {
            pictureBoxState[PICTUREBOXSTATE_useDefaultErrorImage] = true;
            errorImage = defaultErrorImage;
        }
        
        private void ResetImage()
        {
            InstallNewImage(null, ImageInstallationType.DirectlySpecified);
        }
 
        /// <include file='doc\PictureBox.uex' path='docs/doc[@for="PictureBox.RightToLeft"]/*' />
        /// <internalonly/><hideinheritance/>
        /// <devdoc>
        /// </devdoc>
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
        public override RightToLeft RightToLeft {
            get {
                return base.RightToLeft;
            }
            set {
                base.RightToLeft = value;
            }
        }
 
        /// <include file='doc\PictureBox.uex' path='docs/doc[@for="PictureBox.RightToLeftChanged"]/*' />
        /// <internalonly/>
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
        public new event EventHandler RightToLeftChanged {
            add {
                base.RightToLeftChanged += value;
            }
            remove {
                base.RightToLeftChanged -= value;
            }
        }
 
        // Be sure not to re-serialized initial image if it's the default. 
        private bool ShouldSerializeInitialImage()
        {
            return !pictureBoxState[PICTUREBOXSTATE_useDefaultInitialImage];
        }
        
        // Be sure not to re-serialized error image if it's the default. 
        private bool ShouldSerializeErrorImage()
        {
            return !pictureBoxState[PICTUREBOXSTATE_useDefaultErrorImage];
        }
 
        // Be sure not to serialize image if it wasn't directly specified
        // through the Image property (e.g., if it's a download, or an initial
        // or error image)
        private bool ShouldSerializeImage()
        {
            return (imageInstallationType == ImageInstallationType.DirectlySpecified)
                && (Image != null);
        }
        
        /// <include file='doc\PictureBox.uex' path='docs/doc[@for="PictureBox.SizeMode"]/*' />
        /// <devdoc>
        ///    <para>Indicates how the image is displayed.</para>
        /// </devdoc>
        [
        DefaultValue(PictureBoxSizeMode.Normal),
        SRCategory(SR.CatBehavior),
        Localizable(true),
        SRDescription(SR.PictureBoxSizeModeDescr),
        RefreshProperties(RefreshProperties.Repaint)        
        ]
        public PictureBoxSizeMode SizeMode {
            get {
                return sizeMode;
            }
            set {
                //valid values are 0x0 to 0x4
                if (!ClientUtils.IsEnumValid(value, (int)value, (int)PictureBoxSizeMode.Normal, (int)PictureBoxSizeMode.Zoom))
                {
                    throw new InvalidEnumArgumentException("value", (int)value, typeof(PictureBoxSizeMode));
                }
                if (this.sizeMode != value) {
                    if (value == PictureBoxSizeMode.AutoSize) {
                        this.AutoSize = true;
                        SetStyle(ControlStyles.FixedHeight | ControlStyles.FixedWidth, true);
                    }
                    if (value != PictureBoxSizeMode.AutoSize) {
                        this.AutoSize = false;
                        SetStyle(ControlStyles.FixedHeight | ControlStyles.FixedWidth, false);
                        savedSize = Size;
                    }
                    sizeMode = value;
                    AdjustSize();
                    Invalidate();
                    OnSizeModeChanged(EventArgs.Empty);
                }
            }
        }
        
        private static readonly object EVENT_SIZEMODECHANGED = new object();
 
        /// <include file='doc\PictureBox.uex' path='docs/doc[@for="PictureBox.SizeModeChanged"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        [SRCategory(SR.CatPropertyChanged), SRDescription(SR.PictureBoxOnSizeModeChangedDescr)]
        public event EventHandler SizeModeChanged {
            add {
                Events.AddHandler(EVENT_SIZEMODECHANGED, value);
            }
 
            remove {
                Events.RemoveHandler(EVENT_SIZEMODECHANGED, value);
            }
        }
 
        /// <include file='doc\PictureBox.uex' path='docs/doc[@for="PictureBox.TabStop"]/*' />
        /// <internalonly/><hideinheritance/>
        /// <devdoc>
        /// </devdoc>
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
        new public bool TabStop {
            get {
                return base.TabStop;
            }
            set {
                base.TabStop = value;
            }
        }
 
        /// <include file='doc\PictureBox.uex' path='docs/doc[@for="PictureBox.TabStopChanged"]/*' />
        /// <internalonly/>
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
        new public event EventHandler TabStopChanged {
            add {
                base.TabStopChanged += value;
            }
            remove {
                base.TabStopChanged -= value;
            }
        }
 
        /// <include file='doc\PictureBox.uex' path='docs/doc[@for="PictureBox.TabIndex"]/*' />
        /// <internalonly/><hideinheritance/>
        /// <devdoc>
        /// </devdoc>
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
        new public int TabIndex {
            get {
                return base.TabIndex;
            }
            set {
                base.TabIndex = value;
            }
        }     
 
        /// <include file='doc\PictureBox.uex' path='docs/doc[@for="PictureBox.TabIndexChanged"]/*' />
        /// <internalonly/>
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
        new public event EventHandler TabIndexChanged {
            add {
                base.TabIndexChanged += value;
            }
            remove {
                base.TabIndexChanged -= value;
            }
        }
 
        /// <include file='doc\PictureBox.uex' path='docs/doc[@for="PictureBox.Text"]/*' />
        /// <internalonly/><hideinheritance/>        
        /// <devdoc>
        /// </devdoc>
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), Bindable(false)]        
        public override string Text {
            get {
                return base.Text;
            }
            set {
                base.Text = value;
            }
        }
        
        /// <include file='doc\PictureBox.uex' path='docs/doc[@for="PictureBox.TextChanged"]/*' />
        /// <internalonly/>
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
        new public event EventHandler TextChanged {
            add {
                base.TextChanged += value;
            }
            remove {
                base.TextChanged -= value;
            }
        }
        
        /// <include file='doc\PictureBox.uex' path='docs/doc[@for="PictureBox.Enter"]/*' />
        /// <internalonly/><hideinheritance/>
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
        public new event EventHandler Enter {
            add {
                base.Enter += value;
            }
            remove {
                base.Enter -= value;
            }
        }
 
        /// <include file='doc\PictureBox.uex' path='docs/doc[@for="PictureBox.KeyUp"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
        public new event KeyEventHandler KeyUp {
            add {
                base.KeyUp += value;
            }
            remove {
                base.KeyUp -= value;
            }
        }
 
        /// <include file='doc\PictureBox.uex' path='docs/doc[@for="PictureBox.KeyDown"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
        public new event KeyEventHandler KeyDown {
            add {
                base.KeyDown += value;
            }
            remove {
                base.KeyDown -= value;
            }
        }
 
        /// <include file='doc\PictureBox.uex' path='docs/doc[@for="PictureBox.KeyPress"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
        public new event KeyPressEventHandler KeyPress {
            add {
                base.KeyPress += value;
            }
            remove {
                base.KeyPress -= value;
            }
        }
 
        /// <include file='doc\PictureBox.uex' path='docs/doc[@for="PictureBox.Leave"]/*' />
        /// <internalonly/><hideinheritance/>
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
        public new event EventHandler Leave {
            add {
                base.Leave += value;
            }
            remove {
                base.Leave -= value;
            }
        }
 
        /// <include file='doc\PictureBox.uex' path='docs/doc[@for="PictureBox.AdjustSize"]/*' />
        /// <devdoc>
        ///     If the PictureBox has the SizeMode property set to AutoSize, this makes
        ///     sure that the picturebox is large enough to hold the image.
        /// </devdoc>
        /// <internalonly/>
        private void AdjustSize() {
            if (sizeMode == PictureBoxSizeMode.AutoSize) {
                Size = PreferredSize;
            } 
            else {
                Size = savedSize;
            }
        }
 
        private void Animate() {
            Animate(!DesignMode && Visible && Enabled && ParentInternal != null);
        }
 
        private void StopAnimate() {
            Animate(false);
        }
 
        private void Animate(bool animate) {
            if (animate != this.currentlyAnimating) {
                if (animate) {
                    if (this.image != null) {
                        ImageAnimator.Animate(this.image, new EventHandler(this.OnFrameChanged));
                        this.currentlyAnimating = animate;
                    }
                }
                else {
                    if (this.image != null) {
                        ImageAnimator.StopAnimate(this.image, new EventHandler(this.OnFrameChanged));
                        this.currentlyAnimating = animate;
                    }
                }
            }
        }
 
        /// <include file='doc\PictureBox.uex' path='docs/doc[@for="PictureBox.Dispose"]/*' />
        /// <devdoc>
        /// </devdoc>
        /// <internalonly/>
        protected override void Dispose(bool disposing) {
            if (disposing) {
                StopAnimate();
            }
 
            DisposeImageStream();
 
            base.Dispose(disposing);
        }
 
        private void DisposeImageStream() {
            if (localImageStreamReader != null) {
                localImageStreamReader.Dispose();
                localImageStreamReader = null;
            }
            if (uriImageStream != null) {
                uriImageStream.Dispose();
                localImageStreamReader = null;
            }
        }
 
        // Overriding this method allows us to get the caching and clamping the proposedSize/output to
        // MinimumSize / MaximumSize from GetPreferredSize for free.
        internal override Size GetPreferredSizeCore(Size proposedSize) {
            if (image == null) {
                return CommonProperties.GetSpecifiedBounds(this).Size;
            }
            else {
                Size bordersAndPadding = SizeFromClientSize(Size.Empty) + Padding.Size;
                return image.Size + bordersAndPadding;
            }
        }
        
        /// <include file='doc\PictureBox.uex' path='docs/doc[@for="PictureBox.OnEnabledChanged"]/*' />
        protected override void OnEnabledChanged(EventArgs e) {
            base.OnEnabledChanged(e);
            Animate();
        }
        
        private void OnFrameChanged(object o, EventArgs e) {
            if (Disposing || IsDisposed) {
                return;
            }
            // Handle should be created, before calling the BeginInvoke.
            // refer VsWhidbey : 315136.
            if (InvokeRequired && IsHandleCreated) {
                lock (internalSyncObject) {
                    if (handleValid) {
                        BeginInvoke(new EventHandler(this.OnFrameChanged), o, e);
                    }
                    return;
                }
            }
            
            Invalidate();  
        }
 
        [EditorBrowsable(EditorBrowsableState.Advanced)]
        protected override void OnHandleDestroyed(EventArgs e) {
            lock (internalSyncObject) {
                handleValid = false;
            }
            base.OnHandleDestroyed(e);
        }
 
        [EditorBrowsable(EditorBrowsableState.Advanced)]
        protected override void OnHandleCreated(EventArgs e) {
            lock (internalSyncObject) {
                handleValid = true;
            }
            base.OnHandleCreated(e);
        }
 
        /// <include file='doc\PictureBox.uex' path='docs/doc[@for="PictureBox.OnLoadCompleted"]/*' />
        protected virtual void OnLoadCompleted(AsyncCompletedEventArgs e)
        {
            AsyncCompletedEventHandler handler = (AsyncCompletedEventHandler)(Events[loadCompletedKey]);
            if (handler != null)
            {
                handler(this, e);
            }
        }
 
        /// <include file='doc\PictureBox.uex' path='docs/doc[@for="PictureBox.OnLoadProgressChanged"]/*' />
        protected virtual void OnLoadProgressChanged(ProgressChangedEventArgs e)
        {
            ProgressChangedEventHandler handler = (ProgressChangedEventHandler)(Events[loadProgressChangedKey]);
            if (handler != null)
            {
                handler(this, e);
            }
        }
 
        /// <include file='doc\PictureBox.uex' path='docs/doc[@for="PictureBox.OnPaint"]/*' />
        /// <devdoc>
        ///     Overridden onPaint to make sure that the image is painted correctly.
        /// </devdoc>
        /// <internalonly/>
        protected override void OnPaint(PaintEventArgs pe) {
 
            if (pictureBoxState[PICTUREBOXSTATE_needToLoadImageLocation])
            {
                try
                {
                    if (WaitOnLoad)
                    {
                        Load();
                    }
                    else
                    {
                        LoadAsync();
                    }
                }
                catch (Exception ex)
                {   //Dont throw but paint error image LoadAsync fails....
                    // Microsoft FXCOP 
 
 
                    if (ClientUtils.IsCriticalException(ex))
                    {
                        throw;
                    }
                    image = ErrorImage;
                }
            }
            
            if (image != null) {
                Animate();
                ImageAnimator.UpdateFrames(this.Image);
                
                // Error and initial image are drawn centered, non-stretched.
                Rectangle drawingRect =
                    (imageInstallationType == ImageInstallationType.ErrorOrInitial)
                    ? ImageRectangleFromSizeMode(PictureBoxSizeMode.CenterImage)
                    : ImageRectangle;
                
                pe.Graphics.DrawImage(image, drawingRect);
                
            }
 
            // Windows draws the border for us (see CreateParams)
            base.OnPaint(pe); // raise Paint event
        }
 
 
        /// <include file='doc\PictureBox.uex' path='docs/doc[@for="PictureBox.OnVisibleChanged"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        protected override void OnVisibleChanged(EventArgs e) {
            base.OnVisibleChanged(e);
            Animate();
        }
 
        /// <include file='doc\PictureBox.uex' path='docs/doc[@for="PictureBox.OnParentChanged"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        protected override void OnParentChanged(EventArgs e) {
            base.OnParentChanged(e);
            Animate();
        }
 
        /// <include file='doc\PictureBox.uex' path='docs/doc[@for="PictureBox.OnResize"]/*' />
        /// <devdoc>
        ///     OnResize override to invalidate entire control in Stetch mode
        /// </devdoc>
        /// <internalonly/>
        protected override void OnResize(EventArgs e) {
            base.OnResize(e);
            if (sizeMode == PictureBoxSizeMode.Zoom || sizeMode == PictureBoxSizeMode.StretchImage || sizeMode == PictureBoxSizeMode.CenterImage || BackgroundImage != null) {
                Invalidate();
            }
            savedSize = Size;
        }
 
        /// <include file='doc\PictureBox.uex' path='docs/doc[@for="PictureBox.OnSizeModeChanged"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        protected virtual void OnSizeModeChanged(EventArgs e) {
            EventHandler eh = Events[EVENT_SIZEMODECHANGED] as EventHandler;
            if (eh != null) {
                eh(this, e);
            }
        }
 
        /// <include file='doc\PictureBox.uex' path='docs/doc[@for="PictureBox.ToString"]/*' />
        /// <devdoc>
        ///     Returns a string representation for this control.
        /// </devdoc>
        /// <internalonly/>
        public override string ToString() {
 
            string s = base.ToString();
            return s + ", SizeMode: " + sizeMode.ToString("G");
        }
 
        /// <include file='doc\PictureBox.uex' path='docs/doc[@for="PictureBox.WaitOnLoad"]/*' />
        [
        SRCategory(SR.CatAsynchronous),
        Localizable(true),
        DefaultValue(false),
        SRDescription(SR.PictureBoxWaitOnLoadDescr)
        ]
        public bool WaitOnLoad {
            get {
                return pictureBoxState[PICTUREBOXSTATE_waitOnLoad];
            }
            set {
                pictureBoxState[PICTUREBOXSTATE_waitOnLoad] = value;
            }
        }
 
        private enum ImageInstallationType
        {
            DirectlySpecified,
            ErrorOrInitial,
            FromUrl
        }
 
        /// <include file='doc\PictureBox.uex' path='docs/doc[@for="PictureBox.ISupportInitialize.BeginInit"]/*' />
        /// <internalonly/>
        void ISupportInitialize.BeginInit()
        {
            pictureBoxState[PICTUREBOXSTATE_inInitialization] = true;
        }
 
        /// <include file='doc\PictureBox.uex' path='docs/doc[@for="PictureBox.ISupportInitialize.EndInit"]/*' />
        /// <internalonly/>
        void ISupportInitialize.EndInit()
        {
            Debug.Assert(pictureBoxState[PICTUREBOXSTATE_inInitialization]);
 
            // Need to do this in EndInit since there's no guarantee of the
            // order in which ImageLocation and WaitOnLoad will be set.
            if (ImageLocation != null && ImageLocation.Length != 0 && WaitOnLoad)
            {
                // Load when initialization completes, so any error will occur synchronously
                Load();
            }
            
            pictureBoxState[PICTUREBOXSTATE_inInitialization] = false;
        }
    }
}