Foreach循环可创建100个按钮,同时绘制所有按钮以防止闪烁 [英] Foreach loop to create 100 buttons, painting all buttons at same time as to prevent flicker

查看:105
本文介绍了Foreach循环可创建100个按钮,同时绘制所有按钮以防止闪烁的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我的扫雷游戏中,我需要动态创建控件,以便在简单-中-硬之间切换。假设出于问题的考虑,硬包含100个按钮。

In my minesweeper game I need to dynamically create controls in order to shift between easy - medium - hard. Let's say for the sake of the question hard consists of 100 buttons.

这是我创建它们的方式:

This is how I'm creating them:

this.SuspendLayout(); //Creating so many things that I assume it would be faster to suspend and resume.
foreach (string i in playingField)
{

    Button button = new Button();
    button.Name = i;
    button.FlatStyle = System.Windows.Forms.FlatStyle.Popup;
    button.Margin = new Padding(0);
    button.TabIndex = 0;
    button.Location = new System.Drawing.Point(3, 3);
    button.Size = new System.Drawing.Size(25, 25);
    button.BackgroundImage = blockImage; //##//
    button.MouseDown += new System.Windows.Forms.MouseEventHandler(this.GetUnderSide);
    button.UseVisualStyleBackColor = false;
    minesPanel.Controls.Add(button); //Adding the controls to the container
}
this.ResumeLayout(); //Refer to above. Correct me if I'm wrong please.

如您所见,我正在通过for循环创建所有控件,然后添加它们。导致每个按钮一次绘制一次。我还尝试了 name.Controls.AddRange(arrayButtons),它仍然导致相同的问题。单独绘制。

As you can see I'm creating all of the controls through a for loop, and then adding them. It results in each button being painted once at a time. I also tried name.Controls.AddRange(arrayButtons) and it still resulted in the same problem. Individual painting.

我需要的是一种创建所有项目,然后将其全部绘制的方法,例如使用 DoubleBuffered ,但是a,那也不行。

What I need is a way to create all items, and then paint them ALL after, like painting a single bitmap with DoubleBuffered, but alas, that doesn't work either.

此外,我已经尝试过:

    protected override CreateParams CreateParams
    {
        get
        {
            // Activate double buffering at the form level.  All child controls will be double buffered as well.
            CreateParams cp = base.CreateParams;
            cp.ExStyle |= 0x02000000;   // WS_EX_COMPOSITED
            return cp;
        }
    }

这类作品。它仅在应用程序启动时起作用。但是,鉴于我将在运行时更改网格大小并添加更多控件,因此这不是一个可行的选择。

Which kind of works. It only works on application startup. However, given that I will be changing grid sizes and adding more controls at runtime, it is not a viable option.

有人告诉我它就像使用 Graphics 类中的方法一样容易。问题是,我不知道从哪里开始。

I've been told it's as easy as using the methods in the Graphics class. Problem is, I don't know where to start.

如果有人可以提供一些帮助同时绘制所有控件,那将是很棒的。

If anyone could provide some help on getting all my controls to be painted at the same time, that would be great.

推荐答案

不幸的是,WinForms不喜欢拥有太多控件,尤其是当您进入数百个控件时。您永远不能同时拥有每个控件的画图,因为每个控件都会向Windows发送自己的画图消息。

Sadly, WinForms does not like to have too many controls, especially when you get into the hundreds. You can never have each control paint at the same time since each control will send its own paint message to windows.

我认为最好的方法是接近MineSweeper这样的游戏板就是使用一个控件并绘制按钮网格。

I think the best way to approach a game board like MineSweeper is to just use one control and draw the grid of buttons.

简单的Mine类:

public class Mine {
  public Rectangle Bounds { get; set; }
  public bool IsBomb { get; set; }
  public bool IsRevealed { get; set; }
}

然后创建一个新控件,从Panel控件继承并设置防止任何闪烁的DoubleBuffered属性:

Then create a new control where you inherit from the Panel control and set the DoubleBuffered property to prevent any flicker:

public class MineSweeperControl : Panel {
  private int columns = 16;
  private int rows = 12;
  private Mine[,] mines;

  public MineSweeperControl() {
    this.DoubleBuffered = true;
    this.ResizeRedraw = true;

    // initialize mine field:
    mines = new Mine[columns, rows];
    for (int y = 0; y < rows; ++y) {
      for (int x = 0; x < columns; ++x) {
        mines[x, y] = new Mine();
      }
    }
  }

  // adjust each column and row to fit entire client area:
  protected override void OnResize(EventArgs e) {
    int top = 0;
    for (int y = 0; y < rows; ++y) {
      int left = 0;
      int height = (this.ClientSize.Height - top) / (rows - y);
      for (int x = 0; x < columns; ++x) {
        int width = (this.ClientSize.Width - left) / (columns - x);
        mines[x, y].Bounds = new Rectangle(left, top, width, height);
        left += width;
      }
      top += height;
    }
    base.OnResize(e);
  }

  protected override void OnPaint(PaintEventArgs e) {
    e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
    for (int y = 0; y < rows; ++y) {
      for (int x = 0; x < columns; ++x) {
        if (mines[x, y].IsRevealed) {
          e.Graphics.FillRectangle(Brushes.DarkGray, mines[x, y].Bounds);
        } else {
          ControlPaint.DrawButton(e.Graphics, mines[x, y].Bounds, 
                                  ButtonState.Normal);
        }
      }
    }
    base.OnPaint(e);
  }

  // determine which button the user pressed:
  protected override void OnMouseDown(MouseEventArgs e) {
    for (int y = 0; y < rows; ++y) {
      for (int x = 0; x < columns; ++x) {
        if (mines[x, y].Bounds.Contains(e.Location)) {
          mines[x, y].IsRevealed = true;
          this.Invalidate();
          MessageBox.Show(
            string.Format("You pressed on button ({0}, {1})",
            x.ToString(), y.ToString())
          );
        }
      }
    }
    base.OnMouseDown(e);
  }
}

这篇关于Foreach循环可创建100个按钮,同时绘制所有按钮以防止闪烁的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆