Поток пользовательского интерфейса - единственный поток, который может выполнять действия пользовательского интерфейса!
Одна из проблем, с которой вы столкнетесь, заключается в том, что не-пользовательский поток попытается обновить пользовательский интерфейс. Это может делать только пользовательский интерфейс.
В вашем случае рассмотрите возможность использования BackgroundWorker
для чтения.
Вы можете решить создать класс, производный от BackgroundWorker
, для своей работы. Однако, поскольку ваш BackgroundWorker
не обладает большой функциональностью, так же просто использовать стандарт BackGroundWorker
и его событие DoWork
.
Используйте дизайнер окон Windows для добавления BackgroundWorker. Измените свойства в окне свойств.
WorkerReportsProgress = true;
WorkerSupportsCancel = true;
Реагировать на события:
DoWork = DoBackGroundWork
ProgressChanged = ReportProgress
RunWorkerCompleted = ReportBackgroundWorkCompleted
Или альтернативно используйте конструктор:
private readonly BackGroundWorker backgroundWorker;
public Form1()
{
InitializeComponent();
this.backgroundWorker = new BackgroundWorker
{
WorkerReportsProgress = true,
WorkerSupportsCancellation = true,
};
// Subscribe to events:
backgroundWorker.DoWork += this.DoBackGroundWork;
backgroundWorker.ProgressChanged += this.OnReportProgress;
backgroundWorker.RunWorkerCompleted += this.ReportBackgroundWorkCompleted;
// make sure that the BackgroundWorker is properly disposed if this form is disposed:
if (this.components == null) this.components = new System.ComponentModel.Container();
this.components.Add(this.backgroundWorker);
}
DoBackgroundWork - это функция события, которая выполняет фоновую работу. Функция выполняется фоновым работником. Это не поток пользовательского интерфейса. Не делайте ничего в этой форме. Делайте то, что нужно вашему фоновому работнику. Когда у него есть данные, которые необходимо отобразить, звоните ReportProgress
. Регулярно проверяйте CancellationPending
, чтобы увидеть, должно ли оно перестать работать
private void DoBackGroundWork(object sender, DoWorkEventArgs e)
{
var backgroundWorker = (BackgroundWorker)sender;
While(!backgroundWorker.CancellationPending)
{
// continue producing output
var producedOutput = ...
// report that new output is available:
backgroundWorker.ReportProgress(0, producedOutput);
}
}
Первый параметр в ReportProgress - это число, указывающее ход выполнения этого фонового потока. Он может использоваться пользовательским интерфейсом для заполнения индикатора выполнения. Поскольку вы не знаете, как долго вы будете собирать данные, вы не сможете ввести правильный номер.
OnReportProgress вызывается, когда фоновый работник сообщает о прогрессе
Эта функция выполняется потоком пользовательского интерфейса. Не стесняйтесь делать все, что связано с пользовательским интерфейсом
private void OnReportProgress(object sender, ProgressChangedEventArgs e)
{
// you know the type that is reported as progress,
// it is the type of the produced output
string producedText = (string)e.UserState;
this.AddToRichTextBox(producedText);
}
ReportBackgroundWorkCompleted требуется только в том случае, если вам нужно что-то сделать, если фоновый работник завершит работу. Он выполняется потоком пользовательского интерфейса
private void ReportBackgroundWorkCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// finished background working. Do some cleanup
this.ShowBackgroundWorkerActive(false); // for example: hide ajax loader gif
}
Использование фонового рабочего :
void ShowBackGroundWorkerActive(bool active)
{
// give user indication about active backgroundworker, for instance show ajax loader gif
this.GifBackgroundWorkerActive.Visible = active;
}
bool IsBackGroundWorkerActive => this.GifBackgroundWorkerActive.Visible;
void StartBackgroundWorking()
{
if (this.IsBackgroundWorkerActive) return; // already active
// do some preparations:
this.ShowBackgroundWorkerActive(true);
// start the backgroundworker
this.backgroundWorker.RunWorkerAsync();
}
void CancelBackgroundWorking()
{
this.backgroundWorker.CancelAsync();
}
Перестать работать с фоном, если форма закрыта
Единственное, что вам нужно сделать, это убедиться, что ваше окно не закрыто активным фоновым рабочим. Для этого используйте событие OnFormClosing
.
bool formClosingRequested = false;
void OnFormClosing(object sender, FormClosingEventArgs e)
{
if (this.BackGroundWorkerActive)
{
// can't close right now: need to stop the backgroundWorker first.
// remember that we want to close the form:
this.formClosingRequested = true;
this.CancelBackgroundWorking();
e.Cancel = true;
}
// else, no reason to cancel closing
}
Когда фоновый работник будет завершен, после запроса закрытия, нам придется закрыть форму.
void ReportBackgroundWorkCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// finished background working. Do some cleanup
this.ShowBackgroundWorkerActive(false);
if (this.formClosingRequested)
{
// Close the form. This will lead to a FormClosingEvent
// but this time the background worker won't be active
this.Close();
}
}