我正在使用 .NET 4.0 在 C#中开发 MDI 应用程序。
每个MDI子项都是一个包含选项卡的表单,其中包含DataGridView
的GroupBox。
我实现了一个用于管理线程的类。
这是StartNewThread
类
ThreadManager
方法
public string StartNewThread(ThreadStart threadMethod, string threadName)
{
try
{
Thread thread = new Thread(() => threadMethod());
thread.Name = threadName + " (" + _threadCount++.ToString("D4") + ")";
thread.Start();
_threadList.Add(thread.Name, thread);
return thread.Name;
}
catch (Exception ex)
{
//Log and manage exceptions
}
return null;
}
要创建DataGridViews,我使用了 Oracle Developer Tools for VS 库中的一些向导组件。因此,在创建了DataSource以及DataSet之后,我使用了从DataSource树拖放来拖动表并自动创建DataGridViews。
这是自动创建的子表单后面的实际工作代码。
public partial class ScuoleNauticheForm : Form
{
public ScuoleNauticheForm()
{
InitializeComponent();
}
private void ScuoleNauticheForm_Load(object sender, EventArgs e)
{
// TODO: This line of code loads data into the 'dEVRAC_NauticheDataSet.PERSONALE' table. You can move, or remove it, as needed.
this.PersonaleTableAdapter.Fill(this.DEVRAC_NauticheDataSet.PERSONALE);
// TODO: This line of code loads data into the 'dEVRAC_NauticheDataSet.NATANTI' table. You can move, or remove it, as needed.
this.NatantiTableAdapter.Fill(this.DEVRAC_NauticheDataSet.NATANTI);
// TODO: This line of code loads data into the 'dEVRAC_NauticheDataSet.SCUOLE' table. You can move, or remove it, as needed.
this.ScuoleTableAdapter.Fill(this.DEVRAC_NauticheDataSet.SCUOLE);
}
}
我现在要做的是管理分离线程上的所有加载/查询/插入/更新/删除操作。现在我试图创建一个新的线程来加载数据。
这就是我的尝试。
public partial class ScuoleNauticheForm : Form
{
private readonly ThreadManager _threadManager;
public ScuoleNauticheForm()
{
InitializeComponent();
_threadManager = ThreadManager.GetInstance();
}
private void ScuoleNauticheForm_Load(object sender, EventArgs e)
{
_threadManager.StartNewThread(LoadData, "LoadData");
}
#region DataBind
private void LoadData()
{
// TODO: This line of code loads data into the 'dEVRAC_NauticheDataSet.PERSONALE' table. You can move, or remove it, as needed.
this.PersonaleTableAdapter.Fill(this.DEVRAC_NauticheDataSet.PERSONALE);
// TODO: This line of code loads data into the 'dEVRAC_NauticheDataSet.NATANTI' table. You can move, or remove it, as needed.
this.NatantiTableAdapter.Fill(this.DEVRAC_NauticheDataSet.NATANTI);
// TODO: This line of code loads data into the 'dEVRAC_NauticheDataSet.SCUOLE' table. You can move, or remove it, as needed.
this.ScuoleTableAdapter.Fill(this.DEVRAC_NauticheDataSet.SCUOLE);
}
#endregion
}
它仅适用于一半...没有错误或异常,但如果我使用不同的Thread
加载数据,则DataGridviews不会更新,我不会#&# 39;打开表单时看到任何数据,即使我移动或调整它。否则,使用自动生成的代码,将正确填充DataGridViews。
但是,由于向导还在表单中添加导航栏以浏览记录,我注意到它有效,因为它计算了正确的记录数,我可以使用箭头(第一个,上一个,下一个,最后一个)来移动记录。
这是显示我的表单的图像。 请参阅显示正确记录总数的导航栏(14),并允许我浏览它们。
我需要使用delegates
吗?如果是这样,我认为这将是一团糟...我应该创建多少delegates
和那些方法?还是有另一种解决方案吗?
- 更新1 -
我知道UI线程由.NET自动管理,因此程序员不需要使用代码来管理它们。那么,它应该是与管理中内置的.NET UI线程同步的问题吗?也许我的Form.Load()
启动的线程会干扰.NET管理的UI线程?
- 更新2 -
我试图实施faby提出的解决方案。我用Thread
逻辑替换了Task
逻辑。应用程序的行为是相同的,因此使用Thread
的所有内容现在也与Task
一起使用。 但问题仍然存在。由于我在.NET 4.0 而不是.NET 4.5,我无法使用async和await。所以我不知道用这种方法UI是否能正常工作。
任何其他建议对.NET 4.0有效?
答案 0 :(得分:1)
你考虑BackgroundWorker Class的选项吗?
实施DoWork
和ProgressChanged
您可以在DoWork
中执行您在后台线程中执行的操作,在ProgressChanged
中可以更新UI
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
//long running task
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
//update the UI components
}
更新1
另一种解决方案可能是这样的
public Task LoadDataAsync()
{
return Task.Factory.StartNew( () =>
{
//code to fill your datagridview
});
}
然后
public async Task ChangeUIComponents()
{
await LoadDataAsync();
// now here you can refresh your UI elements
}
更新2
在框架4.0中使用async / await尝试使用this NugetPackage(Microsoft.Bcl.Async
)
答案 1 :(得分:0)
我终于找到了一个解决方案,而不使用async / await 和其他库。
问题是我在新Fill()
内执行了TableAdapter
Task
方法,所以我需要使用InvokeRequired
将绑定源数据源设置为{{} 1}}在正确的主题中。
所以我使用了DataTable
。我更改了在新任务上调用的方法,并调用其他3个方法(每个delegates
填充一个),调用DataGridView
实现Fill()
检查。
现在我看到UI的创建,然后在几秒钟之后,异步填充DataGridViews。
这篇文章很有用:Load data from TableAdapter async
感谢@faby建议使用Task而不是Thread。这不是解决方案,但它是一种更好的线程化方法。
这是最终工作代码。
InvokeRequired
- 更新1 -
如果新任务调用的方法是public partial class ScuoleNauticheForm : Form
{
private readonly TaskManager _taskManager;
public ScuoleNauticheForm()
{
InitializeComponent();
_taskManager = TaskManager.GetInstance();
}
private void ScuoleNauticheForm_Load(object sender, EventArgs e)
{
_taskManager.StartNewTask(LoadData);
}
#region Delegates
public delegate void FillPersonaleCallBack();
public delegate void FillNatantiCallBack();
public delegate void FillScuoleCallBack();
#endregion
#region DataBind
private void LoadData()
{
FillPersonale();
FillNatanti();
FillScuole();
}
public void FillPersonale()
{
if (PersonaleDataGridView.InvokeRequired)
{
FillPersonaleCallBack d = new FillPersonaleCallBack(FillPersonale);
Invoke(d);
}
else
{
this.PersonaleTableAdapter.Fill(this.DEVRAC_NauticheDataSet.PERSONALE);
}
}
public void FillNatanti()
{
if (NatantiDataGridView.InvokeRequired)
{
FillNatantiCallBack d = new FillNatantiCallBack(FillNatanti);
Invoke(d);
}
else
{
this.NatantiTableAdapter.Fill(this.DEVRAC_NauticheDataSet.NATANTI);
}
}
public void FillScuole()
{
if (ScuoleDataGridView.InvokeRequired)
{
FillScuoleCallBack d = new FillScuoleCallBack(FillScuole);
Invoke(d);
}
else
{
this.ScuoleTableAdapter.Fill(this.DEVRAC_NauticheDataSet.SCUOLE);
}
}
#endregion
}
且没有任何参数,则可以使用void
简化上述代码。应用程序的行为是相同的。
这是代码的简化版。
Invoke((MethodInvoker) MethodName)