Есть основной поток, в котором я создаю все Controls в winforms. И есть второй поток, который вызывается через срабатывание события. В результате этого события мне нужно изменить значение DataSource у DataGridView. В результате возникает ошибка, что доступ к контролу пытается получить не основной поток, в котором был создан контрол.
Решением является использование методов Invoke\BeginInvoke. Но саму суть я не понимаю этих методов и как их реализовать в коде.
В создаваемом втором потоке используется функция доступа к dgv
private void RefreshTables()
{
try
{
if(con.State == ConnectionState.Closed)
{
con.Open();
}
sql = "select rowid, * from OpenPos";
adapOpenPos = new SQLiteDataAdapter(sql, con);
dsOpenPos = new DataSet();
adapOpenPos.Fill(dsOpenPos);
dataGridView1.DataSource = dsOpenPos.Tables[0];
dataGridView1.Columns[0].Visible = false;
dataGridView1.Columns[15].Visible = false;
dataGridView1.Update();
con.Close();
if (con.State == ConnectionState.Closed)
{
con.Open();
}
sql = "select rowid, * from ClosePos";
adapClosePos = new SQLiteDataAdapter(sql, con);
dsClosePos = new DataSet();
adapClosePos.Fill(dsClosePos);
dataGridView2.DataSource = dsClosePos.Tables[0];
dataGridView2.Columns[0].Visible = false;
dataGridView2.Columns[14].Visible = false;
dataGridView2.Update();
con.Close();
}
catch (Exception e)
{
MessageBox.Show(e.Message);
}
}
Ответ
Суть метода Invoke довольно проста - он принимает делегат и выполняет его в том потоке, в котором был создан элемент управления, у которого вызывается Invoke. Как вы могли заметить, если обращаться к контролам в WinForms не из того потока, в котором они были созданы, будет выброшено исключение. Соответственно, метод Invoke полезен в случаях, когда необходимо работать с контролом из других потоков. Метод BeginInvoke делает то же самое, но асинхронно.
Небольшой пример использования Invoke
private void ButtonInvoke_Click(object sender, EventArgs e)
{
var myThread = new Thread(ThreadFunction);
myThread.Start(); //метод выполняется в другом потоке
}
private void ThreadFunction()
{
Thread.Sleep(1000);
Action action = () => listBox1.Items.Add("value");
// Свойство InvokeRequired указывает, нeжно ли обращаться к контролу с помощью Invoke
if (InvokeRequired)
Invoke(action);
else
action();
}
Стоит также отметить, что async/await, добавленные в C# 5, позволяют обойтись без Invoke
private async void ButtonAsync_Click(object sender, EventArgs e)
{
listBox1.Items.Add("first");
await Task.Run(async () =>
{
await Task.Delay(1000);
});
// этот код будет продолжен в UI потоке,
// и здесь нет необходимости использовать Invoke
listBox1.Items.Add("second");
}
Комментариев нет:
Отправить комментарий