C# 窗口表单冻结和数字输出
本文关键字:数字输出 冻结 表单 窗口 | 更新日期: 2023-09-27 18:36:33
我正在用C#做一个项目,我必须制作控制台应用程序和Windows窗体应用程序。我已经完成了控制台应用程序,但现在我在将其"转换"为Windows窗体时遇到了问题。应用程序必须计算来自 infite 系列的欧拉数。我的问题是当我启动它时,它会冻结并且不会在文本框中给我任何输出。我想要的输出当然是数字。这是代码本身。我希望在没有 gui 的情况下理解它不会有问题。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Eulerovo_cislo_windows_forms
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
int konst = Int32.Parse(textBox1.Text);
double euler = 1;
int variable = 0;
int index = 0;
int factorial = 0;
double absolute_error;
variable = konst;
richTextBox1.Text = ("---ongoing calculations---");
while (true)
{
try
{
if (konst < 1 || konst > 30) throw new ArgumentOutOfRangeException("you have not entered a value between 1 and 30");
while (variable > 0)
{
if (variable == 1)
factorial = 1;
else
{
index = variable - 1;
faktorial = variable;
while (index > 0)
{
factorial *= index;
index--;
}
}
promenna--;
euler += Convert.ToDouble(1) / factorial;
richTextBox1.Multiline = true;
richTextBox1.Text = euler.ToString();
}
}
catch (ArgumentOutOfRangeException ae) // exceptions
{
MessageBox.Show(ae.Message);
break;
}
catch (FormatException fe)
{
MessageBox.Show(fe.Message);
break;
}
finally
{
absolute_error = EulerNumberError.error(euler);
textBox3.Text = absolute_error.ToString();
textBox2.Text = euler.ToString();
}
}
}
private void button2_Click(object sender, EventArgs e)
{
Application.Exit();
}
}
class EulerNumberError // absolute error
{
public static double error(double a)
{
return Math.E - a;
}
}
}
需要在单独的线程中调用它,并在 UI 线程中更新文本框。您的 UI 被冻结,因为您占用了主线程,并且永远不会让您的 UI 有机会更新显示。以下是您可以使用后台工作者尝试的内容。
private void button1_Click(object sender, EventArgs e)
{
int konst = Int32.Parse(textBox1.Text);
double euler = 1;
int variable = 0;
int index = 0;
int factorial = 0;
double absolute_error;
variable = konst;
richTextBox1.Text = ("---ongoing calculations---");
richTextBox1.Multiline = true;
BackgroundWorker worker = new BackgroundWorker();
worker.ProgressChanged += (s, e) =>
{
string s = e.UserState as string;
richTextBox1.Text = s;
};
worker.RunWorkerCompleted += delegate
{
textBox3.Text = absolute_error.ToString();
textBox2.Text = euler.ToString();
};
worker.DoWork += delegate
{
while (true)
{
try
{
if (konst < 1 || konst > 30) throw new ArgumentOutOfRangeException("you have not entered a value between 1 and 30");
while (variable > 0)
{
if (variable == 1)
factorial = 1;
else
{
index = variable - 1;
faktorial = variable;
while (index > 0)
{
factorial *= index;
index--;
}
}
promenna--;
euler += Convert.ToDouble(1) / factorial;
worker.ReportProgress(0, euler.ToString());
}
}
catch (ArgumentOutOfRangeException ae) // exceptions
{
MessageBox.Show(ae.Message);
break;
}
catch (FormatException fe)
{
MessageBox.Show(fe.Message);
break;
}
finally
{
absolute_error = EulerNumberError.error(euler);
}
}
}
worker.RunWorkerAsync();
}
private void button2_Click(object sender, EventArgs e)
{
Application.Exit();
}
}
因此,您有一个答案,显示了如何使用BackgroundWorker
执行此操作。但是,正如 Servy 所建议并且我同意的那样,在当前版本的 .NET 中,最好使用更现代的 async
/await
习语来实现这一点。当然,在这样做时,实际利用这个成语很重要。
下面是执行此操作的 Click
事件处理程序的版本:
private async void button1_Click(object sender, EventArgs e)
{
button1.Enabled = false;
int konst = Int32.Parse(textBox1.Text);
double euler = 1;
int variable = 0;
int index = 0;
int factorial = 0;
double absolute_error;
variable = konst;
richTextBox1.Multiline = true;
richTextBox1.ReadOnly = true;
richTextBox1.Text = ("---ongoing calculations---");
while (true)
{
try
{
if (konst < 1 || konst > 30)
{
throw new ArgumentOutOfRangeException("you have not entered a value between 1 and 30");
}
while (variable > 0)
{
await Task.Run(() =>
{
int iterationMax = 100;
while (variable > 0 && iterationMax-- > 0)
{
if (variable == 1)
{
factorial = 1;
}
else
{
index = variable - 1;
factorial = variable;
while (index > 0)
{
factorial *= index;
index--;
}
}
//promenna--;
euler += 1d / factorial;
}
});
richTextBox1.Text = euler.ToString();
}
}
catch (ArgumentOutOfRangeException ae) // exceptions
{
MessageBox.Show(ae.Message);
break;
}
catch (FormatException fe)
{
MessageBox.Show(fe.Message);
break;
}
finally
{
absolute_error = EulerNumberError.error(euler);
textBox3.Text = absolute_error.ToString();
textBox2.Text = euler.ToString();
}
}
button1.Enabled = true;
}
一些注意事项:
- 我
- 不得不注释掉减少"promenna"的行,我不得不将语句
faktorial = variable;
更改为factorial = variable;
。我没有费心去仔细看这里的数学,所以我真的不知道这些是否是适当的变化。由于发布的代码从未修改variable
值,因此算法不可能实际完成,因此我无法知道这些更改是否接近正确。
外部循环的每次迭代 - 所花费的时间对于每次迭代的 UI 更新来说实在太小了。如果尝试在每次迭代时更新,窗口的消息队列将被填满并导致程序暂时停止响应。因此,我为工作添加了一个中间计数器,以确保它在每次外部迭代上花费足够的时间,以便 UI 线程可以跟上。在我的计算机上,计数 100 足以缓解该问题,但我能够使用高达 10,000,000 的值,同时仍然在 UI 上看到我认为合理的刷新速度。较高的值(尤其是高于 10,000)效果更好。
- 我添加了在工作进行时禁用按钮的逻辑,以确保不会有用户尝试一次执行多个操作。
- 此方法中有代码用于初始化实际上应该在 VS 设计器中设置的
RichTextBox
对象的属性。
最后,我完全同意 Servey 的观点,即最好抽象数学运算,使其根本不依赖于 UI。 也就是说,它可以提供一些机制,无论是async
的还是基于事件的,以允许 UI 更新,而无需数学代码本身对 UI 一无所知。但是上面应该有效,并说明了在简单场景中使用async
/await
的正确方法。
在
button1_Click
try
块中捕获正常Exception
。似乎您有一些例外,但看不到它,因为只处理ArgumentOutOfRangeException
和FormatException
。而且try-catch
周围有无限循环...