Есть два золотых правила потоковой передачи в Windows Forms:
Чтобы взаимодействовать с пользовательским интерфейсом из другого потока, вам необходимо «маршалировать» вызов потока пользовательского интерфейса, используя делегата и вызывая Control. Вызов
/ BeginInvoke
. Вы можете проверить, нужно ли вам вызывать Invoke
, используя свойство InvokeRequired
, но в наши дни я лично склонен просто делать это в любом случае - особых штрафов нет для вызова, когда вам это не нужно.
Лямбда-выражения в C # 3 (или анонимные методы в C # 2) также делают это намного более приятным.
Например, вы можете использовать:
cbFly.Invoke((MethodInvoker)(() => cbFly.Items.Clear()));
All the скобки немного мешают, так что вы можете добавить такой метод расширения, если вы используете C # 3:
public static void Invoke(this Control control, MethodInvoker action)
{
control.Invoke(action);
}
Тогда вы можете сделать:
cbFly.Invoke(() => cbFly.Items.Clear());
, что намного проще. Обычно вы можете обойтись без использования MethodInvoker
, захватив любые переменные, к которым вам нужно получить доступ в делегате.
См. мое руководство по потокам или Джо Альбахари для подробнее.
В качестве второстепенного вопроса, Я вижу, что вы используете Thread.Abort
- фактически в своем собственном потоке, несмотря на то, что после него есть другие вызовы. Почему? Прерывание любого потока , отличного от вашего , является вызовом типа «только для чрезвычайных ситуаций» (который обычно должен сопровождаться выгрузкой приложения в любом случае), и я не вижу причин для прерывания текущего потока, когда еще есть работа, которую предстоит проделать потом ...
Я всегда находил эту статью полезной в этом конкретном вопросе.
В вашем примере вы пытаетесь изменить различные элементы управления из потока, который этого не сделал. создать элемент управления. Чтобы обойти эту проблему в вашем примере, сделайте это вместо этого (при условии, что метод ShowAllFly () является методом в вашей форме):
public void ShowAllFly()
{
Invoke((MethodsInvoker) delegate {
cbFly.Items.Clear();
cbFly.Items.Add("Uçuş Seçiniz...");
dsFlyTableAdapters.tblFlyTableAdapter _t =
new KTHY.dsFlyTableAdapters.tblFlyTableAdapter();
dsFly _mds = new dsFly();
_mds.EnforceConstraints = false;
dsFly.tblFlyDataTable _m = _mds.tblFly;
_t.Fill(_m);
foreach (DataRow _row in _m.Rows)
{
cbFly.Items.Add(_row["FlyID"].ToString() + "-" +
_row["FlyName"].ToString() + "-" +
_row["FlyDirection"].ToString() + "-" +
_row["FlyDateTime"].ToString());
}
//_Thread.Abort(); // WHY ARE YOU TRYING TO DO THIS?
timer1.Enabled = false;
WaitPanel.Visible = false;
} );
}
Чтобы подчеркнуть мысль, высказанную @Jon Skeet, я закомментировал вызов прерывания нить. Нить закончится сама собой. Нет причин прерывать его таким образом.
Взаимодействие с элементами управления в другом (ui) потоке необходимо вызывать следующим образом:
public delegate void ProcessResultDelegate (строковый результат); void ProcessResult (строковый результат) { если (textBox1.InvokeRequired) { var d = новый ProcessResultDelegate (ProcessResult); d.Invoke (результат); } еще { textBox1.Text = результат; } }