Как же получить доступ к элементам из другого потока в языке программирования C#? Просто! Через свойство InvokeRequired
требуемого элемента узнаём требуется ли вызов из другого потока, и если требуется, через метод Invoke
(синхронно) или BeginInvoke
(асинхронно) требуемого элемента выполняем необходимые действия.
Методы Invoke
и BeginInvoke
принимают делегат (указывающий на функцию).
Свой собственный делегат можно не определять а воспользоваться стандартным делегатом, например, Action
.
Определять отдельно выполняемый метод для делегата так же не обязательно, можно прописать анонимный метод.
Анонимный метод в C# создаётся либо при помощи ключевого слова delegate
так:
delegate(/* параметры */) {/* код */},
либо при помощи лямбда-выражений так:
(/* параметры */) => { /* код */ }
У метода Invoke
(или BeginInvoke
) есть 1 перегрузка, ему первым параметром надо передать делегат указывающий на функцию, а вторым параметром можно передать массив аргументов для этой функции.
Пример 1. Используется стандартный делегат Action
и анонимная функция созданная при помощи ключевого слова delegate
:
if (progressBar1.InvokeRequired) { progressBar1.BeginInvoke(new Action(delegate() { progressBar1.Value = 0; })); } else progressBar1.Value = 0;
Пример 2. Используется стандартный делегат Action
и анонимная функция созданная при помощи лямбда-выражений:
if (listBox1.InvokeRequired) listBox1.BeginInvoke(new Action(() => { listBox1.Items.AddRange(arrRivers); })); else listBox1.Items.AddRange(arrRivers);
Пример 3. Используется пользовательский делегат labelTextDelegate
, созданный под метод LabelText
. В методе LabelText
в метод BeginInvoke
первым параметром передаётся пользовательский делегат labelTextDelegate
, который ссылается (выполняет) на метод LabelText
и вторым параметром передаётся массив аргументов для функции на которую ссылается делегат из первого параметра. В массиве аргументов передаётся параметр text
.
delegate void labelTextDelegate(string text); string text; if (InvokeRequired) BeginInvoke(new labelTextDelegate(LabelText), new object[] {text}); else label1.Text = text; private void LabelText(string text) {}
Пример 4. Метод вызывает сам себя в нужном потоке. Используется пользовательский делегат labelTextDelegate
созданный под метод LabelText
. В методе LabelText
в метод BeginInvoke
первым параметром передаётся пользовательский делегат labelTextDelegate
, который ссылается (выполняет) на метод LabelText
и вторым параметром передаётся массив аргументов для функции на которую ссылается делегат из первого параметра. В массиве аргументов передаётся параметр text
, который был передан методу LabelText
. Таким образом метод LabelText
потокобезопасен т.к. проверяет надо ли его выполнять в другом потоке, и если надо, то выполняет.
delegate void labelTextDelegate(string text); private void LabelText(string text) { if (InvokeRequired) BeginInvoke(new labelTextDelegate(LabelText), new object[] {text}); else label1.Text = text; }
Пример 5. Почти всё тоже самое, что и в примере выше только в Invoke
передаётся переменная-объект делегата.
delegate void AddStackItem(string item); AddStackItem m_addToStackDelegate; private void AddToStackPanel(string us) { m_addToStackDelegate = new AddStackItem(AddToStackPanel); if (stackPanel1.InvokeRequired) { //stackPanel1.BeginInvoke(new Action(() => { stackPanel1.Children.Add(us); })); //неверно stackPanel1.Invoke(m_addToStackDelegate, new object[] {us}); } else stackPanel1.Children.Add(us); }