Я пытаюсь использовать фонового работника для обновления списка, используемого для окна состояния в моей форме на С#. Похоже, он не работает должным образом, когда метод addToStatusLog() вызывается из другого класса вне класса MyForm, даже если я передаю экземпляр формы другому классу, который вызывает элемент обновления addToStatusLog. Вместо этого обновление не происходит до тех пор, пока член класса не завершит свою работу и не вернется обратно в класс MyForm. Возможно, есть лучший подход к созданию окон состояния в реальном времени, которые будут запускаться из любого класса, в который передается MyForm. Я новичок в рабочих потоках, поэтому может кто-нибудь просмотреть и сообщить мне, что я могу делать неправильно или что можно улучшить.
public MyForm()
{
InitializeComponent();
// Setup background task to update listbox status so UI is unaffected
_lListBoxQue = new List<string>();
bw_listBoxBGWorker = new BackgroundWorker();
bw_listBoxBGWorker.DoWork += (o, args) => LstbxThread_doWork();
bw_listBoxBGWorker.RunWorkerCompleted += (o, args) => LstbxThread_completed();
}
private void LstbxThread_doWork()
{
System.Threading.Thread.Sleep(100);
}
private void LstbxThread_completed()
{
// Update listbox
lstStatusBox.BeginUpdate();
lstStatusBox.Items.Clear(); // clear entries
lstStatusBox.Items.AddRange(_lListBoxQue.ToArray());
lstStatusBox.EndUpdate();
}
public String addToStatusLog(String sMsg)
{
_lListBoxQue.Add(sMsg);
if (_lListBoxQue.Count > _iStatusLogMaxLines) // > max?
_lListBoxQue.RemoveAt(0); // remove top element?
if ( !bw_listBoxBGWorker.IsBusy ) // background not busy?
bw_listBoxBGWorker.RunWorkerAsync(); // update listbox in back ground task
System.Threading.Thread.Sleep(100);
return sMsg;
}
Это член, который вызывает другой класс, который несколько раз пытается вызвать addToStatusLog во время процесса, но обновления списка не происходят до тех пор, пока MyClass(this).updateDB() не завершится. Мне нужно видеть обновления в реальном времени, когда работает функция updateDB(). Должен быть способ заставить это работать, я надеюсь...
private void btnUpdateDB_Click(object sender, EventArgs e)
{
if (_bIsUpdateEventRunning == false ) // is event not busy?
{
_bIsUpdateEventRunning = true;
new MyClass(this).updateDB();
_bIsUpdateEventRunning = false;
}
}
Пример класса, вызываемого для обновления списка формы.
Public class MyClass{
private MyForm _pForm;
public MyClass(MyForm pForm){ _pForm= pForm; }
public void updateDB(){
_pForm.addToStatusLog("Hello World");
}
}
Обновленный фикс без фонового рабочего:
public String addToStatusLog(String sMsg)
{
_lListBoxQue.Add(sMsg);
if (_lListBoxQue.Count > _iStatusLogMaxLines) // > max?
_lListBoxQue.RemoveAt(0); // remove top element?
lstStatusBox.BeginUpdate();
lstStatusBox.Items.Clear(); // clear entries
lstStatusBox.Items.AddRange(_lListBoxQue.ToArray());
lstStatusBox.EndUpdate();
Application.DoEvents();
return sMsg;
}
Thread.Sleep
здесь не ответ. Скорее всего, вам понадобится Application.DoEvents
. Это обрабатывает все сообщения, ожидающие в настоящее время в очереди сообщений Windows.
Thread.Sleep
просто говорит потоку перейти в спящий режим на указанное вами количество миллисекунд. Если ваш фоновый рабочий процесс работает в потоке пользовательского интерфейса, вы переводите поток пользовательского интерфейса в спящий режим, и он фактически находится в коматозном состоянии. (Важный: все формы Windows выполняются в потоке пользовательского интерфейса.)
Существуют, конечно, и альтернативные конструкции, в которых задействованы отдельные потоки выполнения. Но у них есть свои проблемы, и вы должны помнить о них, прежде чем слепо идти по этому пути.
Согласитесь, я полностью исключил фоновый рабочий поток для этой задачи и добавил Application.DoEvents после загрузки списка, и это работает отлично. Tnx.
Почему ваш DoWork не работает? Разве нам не нужно видеть метод updateDB(), который вы вызываете, если этот код не работает?