C# GUI 类如何从辅助角色类接收定期更新
本文关键字:角色 定期更新 GUI | 更新日期: 2023-09-27 18:35:19
我正在尝试将我的工作代码与 GUI 代码分离到一个完全不同的类中,但我希望能够向我的 GUI 报告进度更新和文件输出。 例如,我想让我的 GUI 对工人类说,"从串行端口读取十行,但在收到时向我报告你阅读的每件事"。 目前,我最好的方法是让 GUI 循环十次,并在每个循环中向 worker 类发送一个命令来读取一件事并返回它。
我真的更愿意将所有循环保留在我的 worker 类的一侧,因为它将包含有关可用内容的更多信息(实际数据量将是可变的,并且 worker 类已经可以访问可用的数据量,我宁愿不将其发送回 GUI 类以运行循环本身。
我已经调查了后台工作者,但这似乎只报告了在漫长操作期间完成的百分比,没有别的,所以这对我没有多大帮助。 有人知道我如何做到这一点吗?
下面是一个程序的外壳,试图(希望)更好地说明我想做什么。 您将如何编辑代码以执行我的要求?
GUI 的主类:
class Main_Class
{
...
/* Assume in the area we have instantiated these items and placed them on the form:
* Button DoSomething: A button to do something
* TextBox ShowInfo: A text box to report something from the worker class
*/
Worker_Class timewaster = new Worker_Class();
private void buttonDoSomething_Click(object sender, EventArgs e)
{
timewaster.a_lengthy_task();
}
}
单独的辅助角色类:
class Worker_Class
{
...//Various Setup stuff up here
void a_lengthy task()
{
int iteration = 0;
while(iteration < 10)
{
Datetime saveNOW = Datetime.Now; //lets say I report this back to the the GUI to write in that ShowInfo box
Thread.sleep(10000); //To waste time and make this lengthy
//Your code here to facilitate sending saveNOW back to the the Main_Class and display it on the ShowInfo textbox.
iteration++
}
}
}
你需要的是事件。 如果您无法解决 BackgroundWorker
类中内置的内容以使其为您工作,则至少可以在解决方案的功能之后对其进行建模。
工人类应该有一个公共事件IterationComplete
或任何你想称呼它的东西。 可以为其提供一个EventArgs
对象(或扩展 EventArgs 的对象),其中包含与 UI 所需的迭代相关的信息。 可以在每次迭代完成后(或在应进行 UI 修改时)触发该事件。
然后,UI 可以为与该迭代相关的所有 UI 任务订阅事件。
代码示例:
主类:
public class MainClass
{
Worker_Class timewaster = new Worker_Class();
private void buttonDoSomething_Click(object sender, EventArgs e)
{
timewaster.IterationComplete += new EventHandler<Worker_Class.IterationEventArgs>(timewaster_IterationComplete);
timewaster.LengthyTask();
}
void timewaster_IterationComplete(object sender, Worker_Class.IterationEventArgs e)
{
string infoFromWorker = e.iterationNumber;
}
}
工人类:
public class Worker_Class
{
//Various Setup stuff up here
public class IterationEventArgs : EventArgs
{
public string iterationNumber { get; set; }
}
public event EventHandler<IterationEventArgs> IterationComplete;
public void LengthyTask()
{
int iteration = 0;
while (iteration < 10)
{
DateTime saveNOW = DateTime.Now; //lets say I report this back to the the GUI to write in that ShowInfo box
Thread.Sleep(10000); //To waste time and make this lengthy
//Your code here to facilitate sending saveNOW back to the the Main_Class and display it on the ShowInfo textbox.
if (IterationComplete != null)
{
IterationEventArgs args = new IterationEventArgs();
args.iterationNumber = iteration.ToString();
IterationComplete(this, args);
}
iteration++;
}
}
}
对于BackgroundWorker
类来说,这是一个理想的任务。 假设您不使用紧凑的框架,它应该可供您使用。
在BackgroundWorker
上,您可以收听onCompleted
和onProgressUpdated
事件。这些控件已编送到 UI 线程中,您可以使用它们安全地更新 winform/wpf 控件。要显示进度,请在BackgroundWorker
的DoWork()
内调用ReportProgress()
将代码放入单独的线程时,在更新 GUI 控件时需要小心。作为更新控件的线程,必须是 UI 线程。BackgroundWorker 会为您处理此问题。
MSDN - http://msdn.microsoft.com/en-us/library/cc221403(v=vs.95).aspx
让您的Worker_Class
通过事件广播其进度。Main_Class
将订阅此事件,并使用这些信息相应地更新 UI。您可以从这里分支到两个方向。可以使用Control.Invoke
将此事件处理程序封送到 UI 线程上,您可以在其中安全地更新 GUI。或者,您可以让事件处理程序将进度信息保存到共享变量中。然后让您的 UI 通过 System.Windows.Forms.Timer
或 DispatcherTimer
以方便的间隔轮询此变量。在这种情况下,我更喜欢后一种方法。
class YourForm : Form
{
private volatile MyEventArgs progress = null;
private void buttonDoSomething_Click(object sender, EventArgs args)
{
var timewaster = new Worker_Class();
timewaster.ProgressChanged += (sender, args) { progress = args; };
Task.Factory.StartNew(
() =>
{
timewaster.a_lengthy_task();
}
UpdateTimer.Enabled = true;
}
private void UpdateTimer_Tick(object sender, EventArgs args)
{
if (progress != null)
{
if (progress.IsFinished)
{
ShowInfo.Text = "Finished";
UpdateTimer.Enabled = false;
}
else
{
ShowInfo.Text = progress.SaveNow.Value.ToString();
}
}
else
{
ShowInfo.Text = "No progress yet";
}
}
}
class Worker_Class
{
public event EventHandler<MyEventArgs> ProgressChanged;
public Worker_Class()
{
ProgressChanged += (sender, args) => { };
}
public void a_lengthy task()
{
int iteration = 0;
while(iteration < 10)
{
Datetime saveNOW = Datetime.Now;
Thread.sleep(10000);
ProgressChanged(this, new MyEventArgs { SaveNow = saveNOW, IsFinished = false });
iteration++
}
ProgressChanged(this, new MyEventArgs { SaveNow = null, IsFinished = true });
}
}
class MyEventArgs : EventArgs
{
public DateTime? SaveNow { get; set; }
public bool IsFinished { get; set; }
}
在我的后台工作者中,我使用:
this.Invoke((MethodInvoker)delegate
{
//do something in GUI
});
例如,如果要在 GUI 中使用
label.Text = "whatever";
方法内部。