【问题标题】:Update a form in parallel with an installation script?与安装脚本并行更新表单?
【发布时间】:2017-06-08 20:05:44
【问题描述】:
我目前有一个执行特定操作的安装“框架”。我现在需要做的是能够与我的脚本并行调用我的表单。像这样的:
InstallationForm f = new InstallationForm();
Application.Run(f);
InstallSoftware(f);
private static void InstallSoftware(InstallationForm f) {
f.WriteToTextbox("Starting installation...");
Utils.Execute(@"C:\temp\setup.msi", @"-s C:\temp\instructions.xml");
...
f.WriteToTextbox("Installation finished");
目前我可以做到这一点的方法是在InstallSoftware 中添加Form.Shown 处理程序,但这看起来真的很乱。无论如何我可以做得更好吗?
【问题讨论】:
标签:
c#
.net
multithreading
winforms
parallel-processing
【解决方案1】:
您的代码将不起作用,因为 Application.Run(f) 直到表单关闭后才会返回。
您可以使用简化的模型/视图/控制器模式。创建一个具有多个事件的 InstallationFormController 类,例如用于将文本通知写入您的文本框。 InstallationForm 在OnLoad() 方法中注册这些事件,然后调用InstallationFormController.Initialize()。该方法开始您的安装(在工作线程/任务上)。该安装回调方法会触发几个文本事件。
InstallationForm f = new InstallationForm(new InstallationFormController());
Application.Run(f);
internal class InstallationFormController
{
public event EventHandler<DataEventArgsT<string>> NotificationTextChanged;
public InstallationFormController()
{
}
public void Initialize()
{
Task.Factory.StartNew(DoInstallation);
}
private void DoInstallation()
{
...
OnNotificationTextChanged(new DataEventArgsT<string>("Installation finished"));
}
private void OnNotificationTextChanged(DataEventArgsT<string> e)
{
if(NotificationTextChanged != null)
NotificationTextChanged(this, e);
}
}
public class DataEventArgsT<T> : EventArgs
{
...
public T Data { get; set; }
}
internal class InstallationForm : Form
{
private readonly InstallationFormController _controller;
public InstallationForm()
{
InitializeComponent();
}
public InstallationForm(InstallationFormController controller) : this()
{
if(controller == null)
throw new ArgumentNullException("controller")
_controller = controller;
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
_controller.NotificationTextChanged += Controller_NotificationTextChanged;
_controller.Initialize();
}
protected virtual void Controller_NotificationTextChanged(object sender, DataEventArgsT<string> e)
{
if(this.InvokeRequired)
{ // call this method on UI thread!!!
var callback = new EventHandler<DataEventArgsT<string>>(Controller_NotificationTextChanged);
this.Invoke(callback, new object[] {sender, e});
}
else
{
_myTextBox.Text = e.Data;
}
}
...
}