我的要求是需要在alertbox中单击中止按钮时中止后台工作操作(附加图像Export)。因为GetData()将花费更多时间来执行。
如果调用Dowork方法,则无需访问UI元素,这意味着我们需要限制它直到后台工作者完成。所以我放了Application.Current.Dispatcher。如果我删除(Application.current.dispatcher)行,我们可以访问UI元素并执行一些操作,但我们需要在执行dowork事件时限制它。
任何解决方案,
try
{
var backGroundWorker = new CancelSupportedBackgroundWorker { WorkerSupportsCancellation = true };
CancellationTokenSource source = new CancellationTokenSource();
var alertBox = new AlertBox
{
IsBusy = true,
WaitingText ="Export Data"
WaitingHeaderText ="Exporting"
};
alertBox.AbortButton.Click += (obj, args) =>
{
source.Cancel();
backGroundWorker.CancelAsync();
backGroundWorker.Abort();
backGroundWorker.Dispose();
GC.Collect();
};
backGroundWorker.DoWork += (obj, args) =>
{
Appliction.Current.Dispatcher.Invoke(DispatcherPriority.ApplicationIdle, new Action(
delegate
{
table = GetData((CancellationToken)args.Argument);
if (source.Token != default(CancellationToken))
if (source.Token.IsCancellationRequested)
return;
}));
};
backGroundWorker.RunWorkerCompleted += (obj, args) =>
{
alertBox.IsBusy = false;
};
backGroundWorker.RunWorkerAsync(source.Token);
}
提前致谢。
我做了一个编辑,将一个令牌参数传递给Dowork事件
明确要求是:
1)查询操作需要在后台运行
2)我们无法访问其他UI元素,如文件菜单项
3)执行查询时,仅访问alertbox中的中止按钮
如果单击中止按钮,它将自动取消后台操作。
我使用Task.Run()方法进行了编辑
Task backgroundTask = null;
try
{
CancellationTokenSource source = new CancellationTokenSource();
var alertBox = new AlertBox
{
IsBusy = true,
WaitingText ="Export Data"
WaitingHeaderText ="Exporting"
};
alertBox.AbortButton.Click += (obj, arg) =>
{
source.Cancel();
GC.Collect();
};
backgroundTask = Task.Run(() => table = GetFullData(source.Token));
IWorkBook.ImportDataTable(table, true, 1, 1, true);
}
catch (ThreadAbortException)
{
}
我为添加GetFullData()方法做了一个更改
internal DataTable GetFullData(CancellationToken token)
{
DataTable dataTable = new DataTable();
if (connection.State != ConnectionState.Open)
{
connection.Open();
}
var command = connection.CreateCommand();
command.CommandText = query;
if (QueryParameters != null && QueryParameters.Count > 0)
{
foreach (var parameter in QueryParameters)
{
var param = command.CreateParameter();
param.ParameterName = "@" + parameter.Name.TrimStart('@');
if (string.IsNullOrEmpty(parameter.Value))
{
param.Value = DBNull.Value;
}
else
{
param.Value = parameter.Value;
}
command.Parameters.Add(param);
}
command.GetPreparedSql();
}
command.Connection = connection;
if (connection.State != ConnectionState.Open)
{
connection.Open();
}
var reader = command.ExecuteReader();
var dataTable = new DataTable();
if (token != default(CancellationToken))
token.ThrowIfCancellationRequested();
dataTable.Load(reader);
return dataTable;
}
答案 0 :(得分:2)
在更详细地查看您的代码之后,看起来您不需要IProgress。
如果你想在另一个线程上调用任何代码,你应该看看它,但只是基于这个问题,你只需要使用Task.Run
& CancellationToken
。
此代码假设一个名为frmDoWork
的表单,其中包含2个按钮cmdDoWork
和cmdAbort
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsApplication1
{
public partial class frmDoWork : Form
{
CancellationTokenSource cts = null;
Task backgroundTask = null;
public frmDoWork()
{
InitializeComponent();
}
private void WorkToDoInBackgroundThread(IProgress<int> progress, CancellationToken cancellationToken)
{
try
{
for (int i = 0; i < 10; i++)
{
cancellationToken.ThrowIfCancellationRequested();
Task.Delay(1000).Wait(cancellationToken);
progress.Report(i);
System.Diagnostics.Debug.WriteLine($"{i} - {DateTime.Now}");
}
}
catch(OperationCanceledException ex)
{
}
}
private void cmdDoWork_Click(object sender, EventArgs e)
{
cts = new CancellationTokenSource();
Progress <int> prg= new Progress<int>(x => this.Text = $"Iteration - {x}");
backgroundTask = Task.Run(()=> WorkToDoInBackgroundThread(prg, cts.Token));
}
private void cmdAbort_Click(object sender, EventArgs e)
{
cts?.Cancel();
}
}
}
您需要检查任务是否已在运行&amp;决定做什么(取消或等待)。这实际上只是意味着如果在完成任务后将cts重置为null,则检查cts是否为null。
您也可以根据需要处理已取消的例外。这段代码就退出了。
我做了一个编辑,还展示了如何使用IProgress接口对另一个线程进行回调。
这是显示警告框时禁用所有者表单的代码段。适应您的需求。
{
AlertBox alertBox = new AlertBox();
alertBox.Owner = this;
alertBox.Show();
alertBox.Closed += (sender, e) => this.IsEnabled = true;
this.IsEnabled = false;
}
答案 1 :(得分:0)
是的,最后我在isHitTestVisible属性的帮助下找到了解决方案,该属性用于返回某些部分的命中测试结果。
感谢@Ashley Pillay的回复。
Window.isHitTestVisible = false;
alertBox.AbortButton.Click += (obj, args) =>
{
Window.IsHitTestVisible = true;
source.Cancel();
backGroundWorker.CancelAsync();
backGroundWorker.Abort();
backGroundWorker.Dispose();
GC.Collect();
};
backGroundWorker.DoWork += (obj, args) =>
{
table = GetData((CancellationToken)args.Argument);
if (source.Token != default(CancellationToken))
if (source.Token.IsCancellationRequested)
return;
};
backGroundWorker.RunWorkerCompleted += (obj, args) =>
{
Window.IsHitTestVisible = true;
alertBox.IsBusy = false;
};