.gif 在整个循环结束之前不会动画?
.gif doesn't animate until the whole loop ends?
我尝试通过循环在 运行 时间的 winform 中添加 300 个按钮。这需要很多时间,所以我想在循环 运行ning 时显示一个 .gif 加载图像。 loading.gif 仅显示但在循环完成之前不会设置动画。这是为什么?
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();
循环阻塞了 UI 线程,所以 pictureBox1
没有更新。有几种可能性可以解决这个问题:
丑陋的:在你的按钮创建循环中时不时地(即不是每一轮)使用 Application.DoEvents();
。
或者您可以从计时器创建按钮,每次 10 个或 20 个,直到达到 300 个。
或者您可以使用基于线程的启动画面。但是,重要的是,所有按钮都由 UI 线程创建。
或者找到不需要 300 个按钮的更好的解决方案。
可以手动处理动画事件。令人惊讶的是 OnFrameChanged
事件仍然触发,使得动画成为可能。
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;
}
}
}
我尝试通过循环在 运行 时间的 winform 中添加 300 个按钮。这需要很多时间,所以我想在循环 运行ning 时显示一个 .gif 加载图像。 loading.gif 仅显示但在循环完成之前不会设置动画。这是为什么?
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();
循环阻塞了 UI 线程,所以 pictureBox1
没有更新。有几种可能性可以解决这个问题:
丑陋的:在你的按钮创建循环中时不时地(即不是每一轮)使用 Application.DoEvents();
。
或者您可以从计时器创建按钮,每次 10 个或 20 个,直到达到 300 个。
或者您可以使用基于线程的启动画面。但是,重要的是,所有按钮都由 UI 线程创建。
或者找到不需要 300 个按钮的更好的解决方案。
可以手动处理动画事件。令人惊讶的是 OnFrameChanged
事件仍然触发,使得动画成为可能。
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;
}
}
}