【问题标题】:Change label backcolor from a loop in C# WinForm app从 C# WinForm 应用程序中的循环更改标签背景色
【发布时间】:2018-03-21 14:10:19
【问题描述】:

我的应用中有一组标签-

    Label[] labels = new Label[8];      

我想在一个循环中按顺序更改它们的背景色-

    private void btnPrepare_Click(object sender, EventArgs e)
    {
        Application.DoEvents();
        for (int i = 0; i < 8; i++)
        {
           labels[i].BackColor = System.Drawing.Color.Red;
           System.Threading.Thread.Sleep(2000);
        }
    }

但是所有的变化都是一起出现的,而不是依次出现的。

有什么帮助吗?

【问题讨论】:

标签: c# winforms


【解决方案1】:

这样说(快速修正):

private void btnPrepare_Click(object sender, EventArgs e) { 
  //DONE: foreach - no magic numbers (8)
  foreach (var lbl in labels) {
    lbl.BackColor = System.Drawing.Color.Red;
    lbl.Update(); // <- Update == force label repainting

    System.Threading.Thread.Sleep(2000);
  }
}

Application.DoEvents()邪恶的:当你想要绘画。

一个更好的方法是使用Task而不是Thread

// async: we're going to put await in the method
private async void btnPrepare_Click(object sender, EventArgs e) { 
  //DONE: foreach - no magic numbers (8)
  foreach (var lbl in labels) {
    lbl.BackColor = System.Drawing.Color.Red;

    // await: No need in force repainting, messages translating etc.
    await Task.Delay(2000);
  }
}

【讨论】:

    【解决方案2】:

    使用Control.Update() 方法,这将使控件在其客户区内重绘无效区域。

    有两种方法可以重绘表单及其内容:

    1. 您可以将Invalidate() 方法的重载之一与Update() 方法一起使用。

    2. 您可以调用Refresh() 方法,该方法强制控件重绘自身及其所有子项。这相当于将Invalidate() 方法设置为true 并与Update() 一起使用。

    Invalidate() 方法控制绘制或重新绘制的内容。 Update() 方法控制何时进行绘制或重新绘制。如果您同时使用Invalidate()Update() 方法而不是调用Refresh(),那么重绘的内容取决于您使用的Invalidate() 的重载。 Update() 方法只是强制立即绘制控件,但 Invalidate() 方法控制调用 Update() 方法时绘制的内容。

    1)

    private void btnPrepare_Click(object sender, EventArgs e)
    {
        for (int i = 0; i < 8; i++)
        {
           labels[i].BackColor = System.Drawing.Color.Red;
           labels[i].Update();
           System.Threading.Thread.Sleep(2000);
        }
    }
    

    2)

    private void btnPrepare_Click(object sender, EventArgs e)
    {   
        for (int i = 0; i < 8; i++)
        {
            labels[i].BackColor = System.Drawing.Color.Red;
            labels[i].Refresh();
            System.Threading.Thread.Sleep(2000);
        }
    }
    

    如果您使用Application.DoEvents() 方法(这不太可能),它将处理当前在消息队列中的所有Windows messages

    当您运行 Windows 窗体应用程序时,它会创建新窗体,然后等待事件处理。每次表单处理一个事件时,它都会处理与该事件相关的所有代码。所有其他事件都在队列中等待。当您的代码处理事件时,您的应用程序不会响应。例如,如果将另一个窗口拖到顶部,则该窗口不会重新绘制。

    如果您在代码中调用DoEvents(),您的应用程序可以处理其他事件。在您的示例中,将 DoEvents() 添加到您的代码中,以便在另一个迭代到来时重新绘制您的表单。如果您从代码中删除DoEvents(),您的表单将在循环结束之前不会重新绘制。

    但是,与Refresh()Update() 方法不同的是,它会处理所有事件(甚至是不必要的事件)。

    作为补充 - 我想添加this answer,它简要(尽可能)说明为什么DoEvents() 是危险的 供使用。

    【讨论】:

    • 我不太确定你是否充分解释了使用 DoEvents() 的内在危险,但 +1 是少数尝试过的人之一。
    • 谢谢,我已经编辑并添加了一些关于 DoWorker 的链接,这很好地解释了为什么你不应该使用它。
    猜你喜欢
    • 1970-01-01
    • 2010-12-21
    • 2012-08-22
    • 1970-01-01
    • 1970-01-01
    • 2021-03-31
    • 1970-01-01
    • 2019-08-29
    • 1970-01-01
    相关资源
    最近更新 更多