调用不会适当地更新 GUI

本文关键字:更新 GUI 调用 | 更新日期: 2023-09-27 18:32:11

我为气象课写了这篇文章。由于某种原因,文本框没有在 GUI 上正确更新大量(大量光子)。计算完成,但文本框不会更新。

我怀疑问题出在调用 Invoke() 上,但我一辈子都看不出出了什么问题。我尝试使用Invoke()和BeginInvoke()都有类似的结果。

谁能帮助弄清楚我哪里出了问题?

谢谢!

PS>请原谅全局变量。打算以后再清理它们...

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
namespace CloudTransmittance
{
public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void buttonCalculate_Click(object sender, EventArgs e)
    {
        Thread t = new Thread(calculateModel);
        t.Start();

    }
    //number of photons that have gone to albedo, direct, or diffuse transmittance
    private ulong top = 0;
    private ulong direct = 0;
    private ulong diffuse = 0;
    private ulong absorbed = 0;
    private ulong failed = 0;
    private ulong photons = 0;

    private void calculateModel()
    {
        //model variables
        double theta = 0;
        double tauStar = 0;
        double omega = 0;
        double g = 0;
        photons = 0;
        //Get data from form
        theta = Convert.ToDouble(textBoxTheta.Text);
        tauStar = Convert.ToDouble(textBoxTau.Text);
        omega = Convert.ToDouble(textBoxOmega.Text);
        g = Convert.ToDouble(textBoxG.Text);
        photons = Convert.ToUInt64(textBoxPhotons.Text);
        //Clear the progress bar and set its limits
        this.progressBar1.BeginInvoke(
            (MethodInvoker)delegate()
            {
                this.progressBar1.Minimum = 0;
                this.progressBar1.Value = 0;
                this.progressBar1.Maximum = (int)photons;
                this.progressBar1.Step = 1;
            });
        //Clear the text boxes
        this.textBoxAlbedo.Invoke(
            (MethodInvoker)delegate()
             {
                  this.textBoxAlbedo.Text = "";
             });
        this.textBoxDirect.Invoke(
           (MethodInvoker)delegate()
           {
               this.textBoxDirect.Text = "";
           });
        this.textBoxDiffuse.Invoke(
           (MethodInvoker)delegate()
           {
               this.textBoxDiffuse.Text = "";
           });
        this.textBox1.Invoke(
           (MethodInvoker)delegate()
           {
               this.textBox1.Text = "";
           });
        this.textBox2.Invoke(
           (MethodInvoker)delegate()
           {
               this.textBox2.Text = "";
           });
        //convert theta to radians from degrees
        theta *= Math.PI / 180;
        //number of photons that have gone to albedo, direct, or diffuse transmittance
        top = 0;
        direct = 0;
        diffuse = 0;
        absorbed = 0;
        failed = 0;
        //Random number generator
        Random r = new Random();
        double randomValue = 0;
        int count = 1000; //number of iterations of the problem...
        double delta = 0.00001; //close enough to "1" for calculations, since C# random goes from [0, 1) instead of [0, 1]
        //Calculate transmittance
        for (ulong photonCount = 0; photonCount < photons; photonCount++)
        {
            bool scattered = false;
            double newTheta = theta; //needed for looping
            int i = 0; //counting variable used to prevent infinite looping
            for (i = 0; i < count; i++)
            {
                double length = calculateTauP(); //length of the photon's travel
                double newTau = calculateTau(newTheta, length);
                if (newTau < 0)
                {
                    top++; //photon has exited through the top
                    break; //move to the next photon
                }
                else if (newTau > tauStar)
                {
                    //exited through the bottom of the cloud
                    if (scattered == false)
                    {
                        //direct transmittance
                        direct++;
                    }
                    else
                    {
                        //diffuse transmittance
                        diffuse++;
                    }
                    break;
                }
                else
                {
                    //photon is either scattered or absorbed
                    randomValue = r.NextDouble();
                    if (randomValue >= omega)  // || ((omega == 1) && (randomValue >= (omega - delta)) )
                    {
                        //photon absorbed, no longer of interest
                        absorbed++;
                        break;
                    }
                    else
                    {
                        //photon scattered, determine direction
                        scattered = true;
                        newTheta = calculateNewAngle(newTau, newTheta, g, randomValue);
                    }
                }
            }
            if (i >= count)
            {
                failed++;
            }
            this.progressBar1.BeginInvoke(
            (MethodInvoker)delegate()
            {
                this.progressBar1.PerformStep();
            });
        }

        //Update Form values
        displayData();
    }
    private void displayData()
    {
        if (this.textBoxAlbedo.InvokeRequired)
        {
            this.textBoxAlbedo.Invoke(
            (MethodInvoker)delegate()
            {
                this.textBoxAlbedo.Text = ((double)top / (double)photons).ToString();
            });
        }
        else
        {
            textBoxAlbedo.Text = ((double)top / (double)photons).ToString();
        }
        if (this.textBoxDirect.InvokeRequired)
        {
            this.textBoxDirect.Invoke(
               (MethodInvoker)delegate()
               {
                   this.textBoxDirect.Text = ((double)direct / (double)photons).ToString();
               });
        }
        else
        {
            textBoxDirect.Text = ((double)direct / (double)photons).ToString();
        }
        if (this.textBoxDiffuse.InvokeRequired)
        {
            this.textBoxDiffuse.Invoke(
               (MethodInvoker)delegate()
               {
                   this.textBoxDiffuse.Text = ((double)diffuse / (double)photons).ToString();
               });
        }
        else
        {
            textBoxDiffuse.Text = ((double)diffuse / (double)photons).ToString();
        }
        if (this.textBox1.InvokeRequired)
        {
            this.textBox1.Invoke(
               (MethodInvoker)delegate()
               {
                   this.textBox1.Text = absorbed.ToString();
               });
        }
        else
        {
            textBox1.Text = absorbed.ToString();
        }
        if (this.textBox2.InvokeRequired)
        {
            this.textBox2.Invoke(
               (MethodInvoker)delegate()
               {
                   this.textBox2.Text = failed.ToString();
               });
        }
        else
        {
            textBox2.Text = failed.ToString();
        }
    }

    private double calculateNewAngle(double length, double angle, double g, double randomNumber)
    {
        double newAngle = 0;
        double cos = (1 / (2 * g)) * (1 + Math.Pow(g, 2) - Math.Pow(((1 - Math.Pow(g, 2)) / (1 + g * (2 * randomNumber - 1))), 2));
        newAngle += angle + cos;
        while (newAngle >= 2 * Math.PI)
        {
            newAngle -= 2 * Math.PI; //normalize the angle to 0 <= angle < 2PI
        }

        return newAngle;
    }
    private double calculateTauP()
    {
        Random r = new Random();
        double distance = -1 * Math.Log(1 - r.NextDouble());
        return distance;
    }
    private double calculateTau(double angle, double tauP)
    {
        double tau = tauP * Math.Cos(Math.PI/2 - angle);
        return tau;
    }
}

}

调用不会适当地更新 GUI

停止使用 InvokeBeginInvoke 来更新 UI。尽管您可能已经被告知,但这并不是那么好的解决方案。实际上,在这种情况下,您要做的就是使用进度信息更新UI,这可能是最糟糕的解决方案。相反,请让工作线程将其进度信息发布到可与 UI 线程共享的不可变数据结构。然后使用 System.Windows.Forms.Timer 在合理的时间间隔内轮询您的 UI 线程。

public class YourForm : Form
{
  private class ProgressInfo
  {
    public ProgressInfo(ulong top, ulong direct, ulong diffuse, ulong absorbed, ulong failed, ulong photons)
    {
      // Set properties here.
    }
    public ulong Top { get; private set; }
    public ulong Direct { get; private set; }
    public ulong Diffuse { get; private set; }
    public ulong Dbsorbed { get; private set; }
    public ulong Failed { get; private set; }
    public ulong Photons { get; private set; }
  }
  private volatile ProgressInfo progress = null;
  private void calculateModel()
  {
    for (ulong photonCount = 0; photonCount < photons; photonCount++)
    {
      // Do your calculations here.
      // Publish new progress information.
      progress = new ProgressInfo(/* ... */);
    }
  }
  private void UpdateTimer_Tick(object sender, EventArgs args)
  {
    // Get a local reference to the data structure.
    // This is all that is needed since ProgressInfo is immutable
    // and the member was marked as volatile.
    ProgressInfo local = progress;
    this.textBoxAlbedo.Text = ((double)local.Top / (double)local.Photons).ToString();
    this.textBoxDirect.Text = ((double)local.Direct / (double)local.Photons).ToString();
    this.textBoxDiffuse.Text = ((double)local.Diffuse / (double)local.Photons).ToString();
    this.textBox1.Text = local.Absorbed.ToString();
    this.textBox2.Text = local.Failed.ToString();
  }

请注意这里的几件事。

  • 代码更容易理解和遵循。
  • UI 线程可以决定何时以及多久更新一次其控件。
  • 您可以在工作线程上获得更多吞吐量,因为它不必像 Invoke 那样等待来自 UI 的响应。

我经常扯掉这些InvokeBeginInvoke解决方案,因为在许多情况下它们是糟糕的解决方案。使用BackgroundWorker稍微好一些,但它仍然迫使您采用更新 UI 的推送方法(尽管如此,通过幕后的相同封送处理技术)。拉动方法可以(并且通常是)更优雅的解决方案,并且通常更有效。