Fish
Fish

Reputation: 165

.gif doesn't animate until the whole loop ends?

I'm try to add 300 buttons in run-time winform by a loop. It takes a lot of time, so I want to display a .gif loading image when the loop is running. The loading.gif just shows but doesn't animate until the loop finishes. Why is that?

pictureBox1.Load("loading.gif");
pictureBox1.Invalidate();
pictureBox1.Update();

// loop to add buttons
this.SuspendLayout();
for (int i = 0; i < 300; i++)
    // add buttons
this.ResumeLayout();
this.PerformLayout();

Upvotes: 2

Views: 1194

Answers (2)

Loathing
Loathing

Reputation: 5266

It's possible to handle the animation events manually. Surprisingly the OnFrameChanged event still fires, making it possible to do the animation.

public class Form1 : Form {

    Button btn = new Button { Text = "Button", Dock = DockStyle.Top };
    AsyncPictureBox box = new AsyncPictureBox("c:\\temp\\chick_dance.gif") { Dock = DockStyle.Fill };

    public Form1() {
        Controls.Add(box);
        Controls.Add(btn);

        btn.Click += delegate {
            box.AnimateImage = !box.AnimateImage;
            Thread.Sleep(30000);
        };
    }
}

public class AsyncPictureBox : Control {

    Bitmap bitmap = null;
    bool currentlyAnimating = false;
    int frameCount = 0;
    int frame = 0;

    public AsyncPictureBox(String filename) {
        bitmap = new Bitmap(filename);
        this.DoubleBuffered = true;
        frameCount = bitmap.GetFrameCount(System.Drawing.Imaging.FrameDimension.Time);
    }

    public bool AnimateImage {
        get {
            return currentlyAnimating;
        }

        set {
            if (currentlyAnimating == value)
                return;

            currentlyAnimating = value;
            if (value)
                ImageAnimator.Animate(bitmap, OnFrameChanged);
            else
                ImageAnimator.StopAnimate(bitmap, OnFrameChanged);
        }
    }

    // even though the UI thread is busy, this event is still fired
    private void OnFrameChanged(object o, EventArgs e) {
        Graphics g = this.CreateGraphics();
        g.Clear(this.BackColor);
        bitmap.SelectActiveFrame(System.Drawing.Imaging.FrameDimension.Time, frame);
        frame = (frame + 1) % frameCount;
        g.DrawImage(bitmap, Point.Empty);
        g.Dispose();
    }

    protected override void Dispose(bool disposing) {
        base.Dispose(disposing);
        if (disposing) {
            if (bitmap != null)
                bitmap.Dispose();
            bitmap = null;
        }
    }
}

Upvotes: 1

JeffRSon
JeffRSon

Reputation: 11176

The loop blocks the UI thread, so pictureBox1 is not updated. There are several possibilities to solve that:

The ugly one: Use Application.DoEvents(); every now and then (ie not every round) in your button creation loop.

Or you could create buttons from a timer, 10 or 20 every time until you got 300.

Or you could use a thread based splash screen. It's important, however, that all buttons are created by the UI thread.

Or find a better solution that doesn't need 300 buttons.

Upvotes: 4

Related Questions