首页 > 解决方案 > 使用事件获取在单独线程上运行的任务的用户输入

问题描述

我有一个长时间运行的任务,它在一个单独的线程中运行。此方法使用 IProgress 报告进度条。这种方法有很多地方需要用户输入(数据冲突)。发生这种情况时,我正在使用事件(仅摘录),并修改 eventargs 以传达结果。

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 winform,因此不确定问题出在 winform 本身还是 devex 皮肤上,但无论哪种方式都需要对其进行排序。

我已经查看了针对此要求的其他解决方案,包括 如何暂停在工作线程上运行的任务并等待用户输入?,但这需要大量的重构并且会更加冗长。

我的问题是:

  1. 使用事件来获取此反馈是否是一个可行的解决方案,如果是,我如何确保我的表单可靠呈现?
  2. 如果此解决方案(事件)存在无法克服的问题,那么解决此问题的最佳方法是什么?一个例子将不胜感激。

标签: .netwinforms

解决方案


我假设,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);
    }
}

你在这里并不需要一个事件,一个简单的回调函数也可以。类似于 a 的东西Func<[Types used in SyncConflictEventArgs], bool>,您将其传递给您的 cpCloneManager 构造函数。无论如何,您应该将调用同步回 UI 线程。该类Progress<T>自动执行此操作(通过SyncronizationContext在构造函数中使用咳嗽)。


推荐阅读