我有一个带日历和数据网格的窗口 当用户在日历中选择新日期时,我想查询数据库以查询在该日期进行的调用。
public HistoryDialog()
{
InitializeComponent();
worker = new BackgroundWorker();
worker.WorkerSupportsCancellation = true;
worker.DoWork += new DoWorkEventHandler(worker_DoWork);
worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
HistoryGrid.SelectionChanged += new SelectionChangedEventHandler(HistoryGrid_SelectionChanged);
}
void calendar_SelectedDatesChanged(object sender, SelectionChangedEventArgs e)
{
startDate = calendar.SelectedDates.OrderBy(x => x.Date).FirstOrDefault();
endDate = calendar.SelectedDates.OrderByDescending(x => x.Date).FirstOrDefault();
if (endDate != startDate)
SelectedDateTextBlock.Text = String.Format("{0:d MMMM}", startDate) + " - " + String.Format("{0:d MMMM}", endDate);
else
SelectedDateTextBlock.Text = String.Format("{0:d MMMM}", startDate);
SearchInDatabase();
}
private void SearchInDatabase()
{
if (worker.IsBusy)
worker.CancelAsync();
worker.RunWorkerAsync();
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
if ((worker.CancellationPending == true))
{
e.Cancel = true;
return;
}
var CallLog = ct.GetCalllogs(startDate, endDate, userID); // Database query
e.Result = CallLog;
}
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
IList CallLog = e.Result as IList;
foreach (CalllogInfo callInfo in CallLog)
{
chvm.CallHistoryList.Add(callInfo);
}
}
但是当用户在后台工作程序仍在运行时选择新日期时,我的程序崩溃了。
如何停止正在运行的后台工作人员并开始新员工?
答案 0 :(得分:4)
现在我看到你已将WorkerSupportsCancellation
属性设置为true。
现在这实际上并没有取消你的BackgroundWorker它只是允许你调用CancelAsync()
方法。
您需要做的是在您的方法处理中定期检查,以确保从CancellationPending
属性中取消工作。当您在找到它时检查此属性时,可以将事件参数的Cancel属性设置为true。这将在您的RunWorkerCompleted
活动中提供。此时(在RunWorkerCompleted
事件处理程序中),您可以重新启动BackgroundWorker
。
以下是使用非常基本的后台工作程序的示例,该工作程序支持取消并响应取消请求。
public MainWindow()
{
InitializeComponent();
this.DataContext = dataModel;
worker = new BackgroundWorker();
worker.WorkerSupportsCancellation = true;
worker.DoWork += (o, e) =>
{
//do a long running task
for (int i = 0; i < 10; i++)
{
System.Threading.Thread.Sleep(500);
if (worker.CancellationPending)
{
e.Cancel = true;
return;
}
}
};
worker.RunWorkerCompleted += (o, e) =>
{
if (e != null && e.Cancelled)
{
startTheWorker();
return;
}
//TODO: I AM DONE!
};
}
BackgroundWorker worker;
private void Button_Click(object sender, RoutedEventArgs e)
{
if (worker != null && worker.IsBusy)
worker.CancelAsync();
else if(worker != null && !worker.CancellationPending)
startTheWorker();
}
void startTheWorker()
{
if (worker == null)
throw new Exception("How come this is null?");
worker.RunWorkerAsync();
}
正如您所看到的,我们必须完成取消工作人员的所有工作。希望这会有所帮助。
答案 1 :(得分:1)
除了其他答案,我想添加以下内容。以下代码块负责引发错误:
private void SearchInDatabase()
{
if (worker.IsBusy)
worker.CancelAsync();
worker.RunWorkerAsync();
}
当您调用CancelAsync时,会立即继续执行您的代码,而无需等到BackgroundWorker真正停止。这就是为什么在BackgroundWorker仍在运行时调用RunWorkerAsync导致您描述的错误的原因。您应该按如下方式更改代码:
private void SearchInDatabase()
{
if (worker.IsBusy)
worker.CancelAsync();
while(worker.IsBusy)
System.Threading.Thread.Sleep(100);
worker.RunWorkerAsync();
}
这可确保BackgroundWorker在开始新工作之前完成其工作。此外,您需要增强DoWork方法以更频繁地检查CancellationPending属性,以便在取消请求后立即停止BackgroundWorker。
答案 2 :(得分:0)
您的问题来自取消。
当您在ct.GetCalllogs
时,您的代码被屏蔽,不支持取消。
在ct.GetCalllogs
中,您应该验证后台工作程序是否处于取消状态,然后再次使用新值启动。
答案 3 :(得分:0)
当工人正在工作时不允许选择将在不需要解决问题的情况下解决问题:
void calendar_SelectedDatesChanged(object sender, SelectionChangedEventArgs e)
{
// .. cut ..
// disable the calendar and go on
calendar.IsEnabled = false;
SearchInDatabase();
}
private void SearchInDatabase()
{
// No longer needed: selection processing is now "atomic"
// if (worker.IsBusy) worker.CancelAsync();
worker.RunWorkerAsync();
}
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// .. cut ..
calendar.IsEnabled = true;
}
或者您可以将worker.IsBusy
绑定到calendar.IsEnabled
以使其“自动”处理(同时仍然不必担心工作方法的并发执行)。