使用事件在单独的线程上获取任务 运行 的用户输入
Getting user input for a Task running on a separate thread using events
我有一个很长的 运行 任务在单独的线程中运行。此方法报告使用 IProgress 作为进度条。此方法有许多点需要用户输入(数据冲突)。发生这种情况时,我正在使用事件(仅摘录),并修改事件参数以传达结果。
string taskResult = await Task.Run(() =>
{
cpCloneManager cloner = new cpCloneManager(_progress.ReportProgress, srcProvider, srcProjectID, destProvider, destProjectID, transferType);
cloner.SynchConflict += Cloner_SynchConflict;
cloner.CloneProject(destSubscriberId, hsTypesToCopy, true);
}
private void Cloner_SynchConflict(object sender, SyncConflictEventArgs e)
{
if (_progress.IsSplashFormVisible) _progress.CloseWaitForm();
if (e.SrcObject is Role) ResolveRoleConflict(e);
if (e.SrcObject is Project) throw new ApplicationException("There is already an existing project in the target database created from the source project.");
if (!_progress.IsSplashFormVisible)
{
_progress.ShowWaitForm(mx);
}
}
void ResolveRoleConflict(SyncConflictEventArgs e)
{
if (e.DestObject == null) return;
using (popRoleConflictDialog pRCD = new popRoleConflictDialog(e))
{
if (pRCD.ShowDialog() == DialogResult.Cancel)
{
setStatusResubmit();
e.Cancel = true;
}
}
}
public class cpCloneManager : IDisposable
{
public event EventHandler<SyncConflictEventArgs> SynchConflict;
public virtual void OnSynchConflict(SyncConflictEventArgs e) => SynchConflict.Invoke(this, e);
public cpCloneManager(IProgress<(ProgressFormCommandEnum, int, string)> Progress, DatabaseProvider srcDbProvider, int SrcProjectID, DatabaseProvider destDbProvider, int DestProjectID, CloneTransferType CopyType)
{
//...
}
public string CloneProject(int TargetSubscriberId, HashSet<Type> HsTypesToClone = null, bool AddAsNew = false)
{
//...
var e_synch = new SyncConflictEventArgs(p_src, p_dest, dest_subscriberId);
OnSynchConflict(e_synch);
if (e_synch.result = Result.Copy)
{
//...
}
}
}
这通常可以正常工作,但是一些用户在我用于获取用户反馈的 winform 渲染中遇到奇怪的效果。这与显示用户反馈表单的上下文有关。这些表单实际上是 devexpress winforms,所以不确定问题是出在 winform 本身还是 devex 皮肤上,但无论哪种方式都需要对其进行排序。
我已经查看了此要求的其他解决方案,包括
,但这需要大量重构,而且会更冗长。
我的问题是:
- 使用事件来获取此反馈是可行的解决方案吗?如果是,我如何确保我的表单能够可靠地呈现?
- 如果此解决方案(事件)存在无法解决的问题,那么解决此问题的最佳方法是什么?一个例子将不胜感激。
我假设,Cloner_SynchConflict
是表单中的一个方法。尝试更改它:
private void Cloner_SynchConflict(object sender, SyncConflictEventArgs e)
{
if (InvokeRequired)
{
Invoke((EventHandler<SyncConflictEventArgs>)Cloner_SynchConflict, sender, e);
return;
}
if (_progress.IsSplashFormVisible) _progress.CloseWaitForm();
if (e.SrcObject is Role) ResolveRoleConflict(e);
if (e.SrcObject is Project) throw new ApplicationException("There is already an existing project in the target database created from the source project.");
if (!_progress.IsSplashFormVisible)
{
_progress.ShowWaitForm(mx);
}
}
这里你真的不需要事件,一个简单的回调函数也可以。您传递给 cpCloneManager 构造函数的类似 Func<[Types used in SyncConflictEventArgs], bool>
的内容。无论如何,您应该将调用同步回 UI 线程。 Progress<T>
class 自动执行此操作(通过在构造函数中使用 SyncronizationContext
cought)。
我有一个很长的 运行 任务在单独的线程中运行。此方法报告使用 IProgress 作为进度条。此方法有许多点需要用户输入(数据冲突)。发生这种情况时,我正在使用事件(仅摘录),并修改事件参数以传达结果。
string taskResult = await Task.Run(() =>
{
cpCloneManager cloner = new cpCloneManager(_progress.ReportProgress, srcProvider, srcProjectID, destProvider, destProjectID, transferType);
cloner.SynchConflict += Cloner_SynchConflict;
cloner.CloneProject(destSubscriberId, hsTypesToCopy, true);
}
private void Cloner_SynchConflict(object sender, SyncConflictEventArgs e)
{
if (_progress.IsSplashFormVisible) _progress.CloseWaitForm();
if (e.SrcObject is Role) ResolveRoleConflict(e);
if (e.SrcObject is Project) throw new ApplicationException("There is already an existing project in the target database created from the source project.");
if (!_progress.IsSplashFormVisible)
{
_progress.ShowWaitForm(mx);
}
}
void ResolveRoleConflict(SyncConflictEventArgs e)
{
if (e.DestObject == null) return;
using (popRoleConflictDialog pRCD = new popRoleConflictDialog(e))
{
if (pRCD.ShowDialog() == DialogResult.Cancel)
{
setStatusResubmit();
e.Cancel = true;
}
}
}
public class cpCloneManager : IDisposable
{
public event EventHandler<SyncConflictEventArgs> SynchConflict;
public virtual void OnSynchConflict(SyncConflictEventArgs e) => SynchConflict.Invoke(this, e);
public cpCloneManager(IProgress<(ProgressFormCommandEnum, int, string)> Progress, DatabaseProvider srcDbProvider, int SrcProjectID, DatabaseProvider destDbProvider, int DestProjectID, CloneTransferType CopyType)
{
//...
}
public string CloneProject(int TargetSubscriberId, HashSet<Type> HsTypesToClone = null, bool AddAsNew = false)
{
//...
var e_synch = new SyncConflictEventArgs(p_src, p_dest, dest_subscriberId);
OnSynchConflict(e_synch);
if (e_synch.result = Result.Copy)
{
//...
}
}
}
这通常可以正常工作,但是一些用户在我用于获取用户反馈的 winform 渲染中遇到奇怪的效果。这与显示用户反馈表单的上下文有关。这些表单实际上是 devexpress winforms,所以不确定问题是出在 winform 本身还是 devex 皮肤上,但无论哪种方式都需要对其进行排序。
我已经查看了此要求的其他解决方案,包括
我的问题是:
- 使用事件来获取此反馈是可行的解决方案吗?如果是,我如何确保我的表单能够可靠地呈现?
- 如果此解决方案(事件)存在无法解决的问题,那么解决此问题的最佳方法是什么?一个例子将不胜感激。
我假设,Cloner_SynchConflict
是表单中的一个方法。尝试更改它:
private void Cloner_SynchConflict(object sender, SyncConflictEventArgs e)
{
if (InvokeRequired)
{
Invoke((EventHandler<SyncConflictEventArgs>)Cloner_SynchConflict, sender, e);
return;
}
if (_progress.IsSplashFormVisible) _progress.CloseWaitForm();
if (e.SrcObject is Role) ResolveRoleConflict(e);
if (e.SrcObject is Project) throw new ApplicationException("There is already an existing project in the target database created from the source project.");
if (!_progress.IsSplashFormVisible)
{
_progress.ShowWaitForm(mx);
}
}
这里你真的不需要事件,一个简单的回调函数也可以。您传递给 cpCloneManager 构造函数的类似 Func<[Types used in SyncConflictEventArgs], bool>
的内容。无论如何,您应该将调用同步回 UI 线程。 Progress<T>
class 自动执行此操作(通过在构造函数中使用 SyncronizationContext
cought)。