C# 执行后台操作时无法访问AbortButton
我的要求是需要在单击alertbox(附图)中的abort按钮时中止backgroundworker操作。因为GetData()将需要更多的时间来执行 如果调用Dowork方法,则无需访问UI元素,这意味着我们需要限制它,直到backgroundworker完成。如果我删除了(Application.current.dispatcher)行,我们可以访问UI元素并执行一些操作,但我们需要在执行dowork事件时限制它 任何解决办法C# 执行后台操作时无法访问AbortButton,c#,multithreading,backgroundworker,C#,Multithreading,Backgroundworker,我的要求是需要在单击alertbox(附图)中的abort按钮时中止backgroundworker操作。因为GetData()将需要更多的时间来执行 如果调用Dowork方法,则无需访问UI元素,这意味着我们需要限制它,直到backgroundworker完成。如果我删除了(Application.current.dispatcher)行,我们可以访问UI元素并执行一些操作,但我们需要在执行dowork事件时限制它 任何解决办法 try { v
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()方法进行了编辑
我对添加GetFullData()方法进行了更改
在更详细地查看了代码之后,您似乎不需要IProgress 如果您想调用另一个线程上的任何代码,您应该查看它,但根据问题,您只需要使用
Task.Run
&CancellationToken
此代码采用名为frmDoWork
的表单,带有两个按钮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();
}
}
}
是的,最后我在IshittesVisible属性的帮助下找到了解决方案,该属性用于返回某个部分的命中测试结果 感谢@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;
};
我还尝试将Application.current.Dispatcher替换为Dispatcher.CurrentDispatcher。在这里,我可以访问其他UI元素。但我们需要限制这一点,因为执行dowork eventBackgroundWorker是一种非常古老的模式。Net中现在的首选模式是使用Task with IProgress来更新UI。我不需要更新UI。只想在后台执行某些操作时调用abort click事件。任何带有IProgress.IProgress的任务链接都不仅仅用于更新UI,它是一种更简单的模式,用于捕获创建进度实例的线程的上下文,并允许您注册在该线程上下文中运行的回调。创建一个Progress实例,传入调用中止单击事件处理程序的操作回调。然后将IProgress对象传递到Task.Run、Parallel.ForEach等中的回调中。当调用IProgress.Report时,您的回调在正确的上下文中运行。您将进行所有设置后台工作程序的工作,但随后立即使用
调度程序,将其所有工作返回到UI线程上。不知道为什么。嗨@Ashley,我也按照你的代码使用相同的概念。在我的代码中,单击中止按钮,后台操作将被取消。当使用Application.current.Dispatcher行时,它不会被击中。这是我们的问题。无论如何,我会试试这个。我知道代码看起来很相似,但除非你处理的是写得很糟糕的遗留代码,你害怕被破坏,否则任务是现代的方法。如果你真的想继续使用BackgroundWorker,我建议你使用RunWorkerAsync的重载,它需要一个参数。您可以使用该对象传入需要在backGroundWorker.DoWork中调用的任何对象,并通过args参数访问它们。然后,您将不依赖于Application.Current.Dispatcher,因为对象将在后台线程中直接访问。我根据您的建议更改代码,将参数(Source.Token)传递给Dowork方法,但该方法未按预期工作。我尝试了Task.Run(),该方法也未按预期工作。通过未按预期工作,我想你的意思是当你点击按钮时不放弃。请发布您使用Task.Run尝试的代码。请注意,IshitteVisible基本上是告诉窗口忽略其客户端区域中的鼠标事件。窗口仍然可以接收键盘事件以及非客户端区域(边框)中的鼠标事件。用户可以单击“窗口”和“选项卡”来选择各种控件或选择菜单。此外,没有任何视觉指示表明控件不可用。它们看起来是可点击的,但只是忽略点击。设置IsEnabled可防止键盘和鼠标输入,也会使提供视觉反馈的子控件变灰,窗口无法与之连接。感谢@AshleyPillay,如果将IsEnabled设置为false,则可以正常工作。
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();
}
}
}
{
AlertBox alertBox = new AlertBox();
alertBox.Owner = this;
alertBox.Show();
alertBox.Closed += (sender, e) => this.IsEnabled = true;
this.IsEnabled = false;
}
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;
};