public static void AddImageToImageList(this TabControl @this, Image image, string key) { #if NET40 Contract.Requires(image != null); Contract.Requires(!string.IsNullOrEmpty(key)); #endif var imageList = @this.ImageList; #if NET40 Contract.Assert(imageList != null, "Can only work on TabControls with assigned ImageList."); #endif imageList.Images.Add(key, image); if (!ImageAnimator.CanAnimate(image)) { return; } var uniquePart = new Random().Next(); // NOTE: we shuffle the key to prevent users accidentially using the generated images string KeyGenerator(string @base, int index) => @base + "\0" + uniquePart + "\0" + index; var numImages = image.GetFrameCount(FrameDimension.Time); var createdImages = new Dictionary <string, Image>(); for (var i = numImages - 1; i >= 0; --i) { image.SelectActiveFrame(FrameDimension.Time, i); var image2 = (Image)image.Clone(); var subKey = KeyGenerator(key, i); createdImages.Add(subKey, image2); imageList.Images.Add(subKey, image2); } var currentlyShownImageIndex = 0; void OnFrameChangedHandler(object _, EventArgs __) { currentlyShownImageIndex = (currentlyShownImageIndex + 1) % numImages; var currentKey = KeyGenerator(key, currentlyShownImageIndex); // refresh all tabpages using this image @this.SafelyInvoke(() => @this.TabPages.Cast <TabPage>().Where(t => t.ImageKey == key || t.ImageKey.StartsWith(key + "\0")).ForEach(tp => tp.ImageKey = currentKey)); } // start animating ImageAnimator.Animate(image, OnFrameChangedHandler); // remove handler and dispose image on destruction of tabcontrol @this.Disposed += (_, __) => { ImageAnimator.StopAnimate(image, OnFrameChangedHandler); foreach (var kvp in createdImages) { imageList.Images.RemoveByKey(kvp.Key); kvp.Value.Dispose(); } }; }