Человек даже немного поработав с Windows Forms наверняка сталкивался с замечательным исключением System.InvalidOperationException с таким описанием:
{«Cross-thread operation not valid: Control ‘textBox1’ accessed from a thread other than the thread it was created on.»}
Этим сообщением среда выполнения недвусмысленно дает понять, что обращаться к элементам управления пользовательского интерфейса можно только из того потока, который создал этот элемент управления. Такое поведение обусловлено деталями реализации, в частности использованием однопоточной модели аппартаментов (Single-threaded Apartment, STA) и механизма обработки оконных сообщений.
Вот простенький пример кода, который приводит к генерации такого исключения:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
//на форме расположен TextBox и Button.
//В TextBox будет выводиться количество сработок таймера,
//таймер запускается по нажатию на кнопку Button
timer = new Timer(AsyncHandler);
}
private void button1_Click(object sender, EventArgs e)
{
timer.Change(1000, 1000);
}
private void AsyncHandler(object data)
{
tickCount++;
textBox1.Text = tickCount.ToString();
}
private readonly System.Threading.Timer timer;
private int tickCount;
}
Существует вполне простой и понятный способ решения этой проблемы, путем проверки свойства элемента управления InvokeRequired с последующим вызовом метода Invoke.
Вот самый простой и примитивный способ:
private void AsyncHandler(object data)
{
tickCount++;
Action<int> action = DoChangeTicks;
if (InvokeRequired)
{
Invoke(action, tickCount);
}
else
{
action(tickCount);
}
}
private void DoChangeTicks(int count)
{
textBox1.Text = tickCount.ToString();
}
Способ простой, но не совсем удобный. В данном случае вспомогательный метод только увеличивает связность и усложняет понимает решаемой задачи.
Если воспользоваться новшествами C#3.0 в виде лямбда-выражений, можно сделать несколько более удобную реализацию следующего вида:
private void AsyncHandler(object data)
{
tickCount++;
Action action = () => textBox1.Text = tickCount.ToString();
if (InvokeRequired)
{
Invoke(action);
}
else
{
action();
}
}
Уже лучше. Мы избавились от необязательного метода, который неразрывно связан с тем действием, которое выполняет метод AsyncHandler. Но, все же, еще есть над чем подумать.
Следующий вариант решения был честно подсмотрен в неплохой книжке: Bill Wagner «More Effective C#».
/// <summary>
/// Расширения облегчающие работу с элементами управления в многопоточной среде.
/// </summary>
public static class ControlExtentions
{
/// <summary>
/// Вызов делегата через control.Invoke, если это необходимо.
/// </summary>
/// <param name=»control»>Элемент управления</param>
/// <param name=»doit»>Делегат с некоторым действием</param>
public static void InvokeIfNeeded(this Control control, Action doit)
{
if (control.InvokeRequired)
control.Invoke(doit);
else
doit();
}
/// <summary>
/// Вызов делегата через control.Invoke, если это необходимо.
/// </summary>
/// <typeparam name=»T»>Тип параметра делегата</typeparam>
/// <param name=»control»>Элемент управления</param>
/// <param name=»doit»>Делегат с некоторым действием</param>
/// <param name=»arg»>Аргумент делагата с действием</param>
public static void InvokeIfNeeded<T>(this Control control, Action<T> doit, T arg)
{
if (control.InvokeRequired)
control.Invoke(doit, arg);
else
doit(arg);
}
}
С помощью этого вспомогательного класса, реализация метода, взаимодействующего с элементами управления из других потоков, будет следующей:
private void AsyncHandler(object data)
{
tickCount++;
this.InvokeIfNeeded(
() => textBox1.Text = tickCount.ToString()
);
}
C#: зачем нужны InvokeRequired и Invoke?
Добавил(а) microsin
Вы наверное знаете, что нельзя просто так обращаться к графическим элементам управления интерфейса пользователя (GUI формы) из других потоков, таких как BackgroundWorker [2]. Это связано с безопасностью данных потоков, когда конкурентное обращение разными потоками к одним и тем же данным на запись может привести к непредсказуемому поведению программы.
Предотвращают такие неприятные ситуации специальные программные решения. В потоке BackgroundWorker, например, этот функционал инкапсулирован в вызове ReportProgress и обработчике ProgressChanged. Для обращения к элементам интерфейса управления пользователя (к экземпляру класса System.Windows.Forms.Control, и следовательно экземпляру формы окна программы System.Windows.Forms.Form как частности) из сторонних потоков как раз и нужны свойство InvokeRequired и метод Invoke класса Control.
Методы Invoke/BeginInvoke позволяют Вам перепоручить (делегировать, delegate) классу GUI вызов некоторых связанных с интерфейсом пользователя методов. Передаваемые в форму данные (такие как значения параметров) помещаются в некую очередь, поддерживаемую потоком GUI. Поток GUI берет из этой очереди элементы и выполняет реальные вызовы синхронно с другими вызовами обработки GUI. С помощью Invoke вызывающий сторонний поток (не поток GUI) переводится в состояние ожидания, пока вызов не будет завершен потоком GUI. BeginInvoke сделает возврат немедленно в момент реального вызова.
Итак, invoke-методы реализованы в классе Control (и также в экземпляре формы Form1 приложения), и полезно их использовать только тогда, когда Вы вызываете методы формы или обращаетесь к её свойствам (т. е. к классу Control) из потока, который не относится к потоку обработки графического интерфейса формы (GUI).
[InvokeRequired]
Чтобы было понятно, давайте разберемся подробнее. InvokeRequired делает проверку, в каком контексте работает текущий код. Проверка InvokeRequired нужна только тогда, когда вызов/обращение к форме происходит из стороннего потока, не относящегося к GUI.
InvokeRequired всегда вернет true, если это работает контекст чужого потока (не потока GUI), тогда необходимо вызывать Invoke со ссылкой на текущий выполняемый код (как это делается, будет показано на примере ниже). Если это не сделать, то произойдет ошибка времени выполнения:
Недопустимая операция в нескольких потоках: попытка доступа к элементу управления 'имя_элемента_GUI' не из того потока, в котором он был создан.
InvokeRequired всегда вернет false, если работает поток GUI, и в этом случае вызывать Invoke не требуется.
[Invoke]
Добавим ясности: разберем простой конкретный пример с компонентом USB HID (библиотека UsbLibrary), код которого работает в отдельном потоке. Экземпляр класса UsbHidPort myUSB сделан дочерним по отношению к классу формы приложения, и в его обработчике приема данных необходимо отображать принятые данные в ListBox формы. Обработчик приема данных UsbHidPort работает в отдельном потоке, не в потоке GUI формы, поэтому без invoke-функционала добавить данные в ListBox формы формы нельзя. Для потокобезопасной работы в обработчике приема данных UsbHidPort нужно добавить код наподобие следующего:
private void usb_OnDataRecieved(object sender, DataRecievedEventArgs args) { try { string DataSt = null; if (ParentForm.InvokeRequired) { ParentForm.Invoke(new DataRecievedEventHandler(usb_OnDataRecieved), new object[] { sender, args }); } else { // Показываем, что приняли: for (int i = 4; i < args.data.Length; i++) DataSt += (char)(args.data[i]); ParentForm.lbConsole.Items.Add(DataSt); logfile.write(DataSt); } } catch (Exception ex) { logfile.write(ex.Message); } }
Обратите внимание, что вызов InvokeRequired и Invoke формы приложения происходит через переменную ParentForm, добавленную к классу myUSB, которая была предварительно инициализирована в коде загрузки формы после вызова конструктора экземпляра класса mySUB.
namespace WindowsFormsApplication1 { public partial class FormMain : Form { log logfile; public myUSB myusb;
public FormMain() { InitializeComponent(); }
void Form1_Load(object sender, EventArgs e) { myusb = new myUSB(); myusb.ParentForm = this;
... }
//Другие методы формы:
...
}
}
namespace WindowsFormsApplication1 { public class myUSB { byte[] rxdata; int rxidx; public UsbHidPort usb; const int WAIT_ANSWER_TIMEOUT = 500; log logfile; public FormMain ParentForm;
public void RxReset() { rxidx = 0; rxdata[0] = 0; }
//Конструктор public pkrc() { if (null == logfile) logfile = new log(this.GetType().ToString()); if (null == rxdata) rxdata = new byte[256]; RxReset(); if (null == usb) usb = new UsbHidPort(); usb.VendorId = 0x03EB; usb.ProductId = 0x6201; }
~pkrc() { Close(); }
#region Обработка событий USB библиотеки /// < summary> /// Запустится при подключении любого USB устройства /// < /summary> /// < param name="sender">< /param> /// < param name="e">< /param> private void usb_OnDeviceArrived(object sender, EventArgs e) { logfile.write("usb_OnDeviceArrived"); }
/// < summary> /// При отключении любого устройства USB /// < /summary> /// < param name="sender">< /param> /// < param name="e">< /param> private void usb_OnDeviceRemoved(object sender, EventArgs e) { logfile.write("usb_OnDeviceRemoved"); }
/// < summary> /// При подключении определенного устройства USB /// < /summary> /// < param name="sender">< /param> /// < param name="e">< /param> private void usb_OnSpecifiedDeviceArrived(object sender, EventArgs e) { logfile.write("usb_OnSpecifiedDeviceArrived"); } /// < summary> /// При отключении определенного устройства USB /// < /summary> /// < param name="sender">< /param> /// < param name="e">< /param> private void usb_OnSpecifiedDeviceRemoved(object sender, EventArgs e) { if (ParentForm.InvokeRequired) { ParentForm.Invoke(new EventHandler(usb_OnSpecifiedDeviceRemoved), new object[] { sender, e }); } else { logfile.write("UsbHidPort closed"); } }
/// < summary> /// //////////////////////////////////////////////////////////////////////////////// /// Приём данных /// < /summary> /// < param name="sender">< /param> /// < param name="args">Report, принятый от устройства USB HID, содержится /// в args.data< /param> private void usb_OnDataRecieved(object sender, DataRecievedEventArgs args) { try { if (ParentForm.InvokeRequired) { ParentForm.Invoke(new EventHandler(usb_OnDataRecieved), new object[] { sender, e }); } else { string DataSt = null; //Простой вывод в лог данных: for (int i = 0; i < args.data.Length; i++) DataSt += args.data[i].ToString("X2") + " "; logfile.write(DataSt); //Вывод данных в ListBox формы: ParentForm.lbConsole.Items.Add(DataSt); } } catch (Exception ex) { logfile.write(ex.Message); } }
/// < summary> /// При передаче данных в устройство USB /// < /summary> /// < param name="sender">< /param> /// < param name="e">< /param> private void usb_OnDataSend(object sender, EventArgs e) { //logfile.write("usb_OnDataSend"); } #endregion public void Open() { //Инициализация библиотеки USB HID. usb.OnDataRecieved += new DataRecievedEventHandler(this.usb_OnDataRecieved); usb.OnDataSend += new System.EventHandler(this.usb_OnDataSend); usb.OnDeviceArrived += new System.EventHandler(this.usb_OnDeviceArrived); usb.OnDeviceRemoved += new System.EventHandler(this.usb_OnDeviceRemoved); usb.OnSpecifiedDeviceArrived += new System.EventHandler(this.usb_OnSpecifiedDeviceArrived); usb.OnSpecifiedDeviceRemoved += new System.EventHandler(this.usb_OnSpecifiedDeviceRemoved); try { usb.Open(false); if (0 != usb.GetLastError) { logfile.write(String.Format("cannot open USB HID device VID:0x{0:X4} PID:0x{1:X4}", usb.VendorId, usb.ProductId)); } } catch (Exception ex) { logfile.write(ex.Message); } }
public void Close() { usb.Close(); }
public String Received() { string rx = Encoding.GetEncoding(1251).GetString(rxdata).Substring(0, rxidx); if (rx.Contains(DBGMSG)) { if (rx.Contains("\r\n")) { int pos = rx.IndexOf("\r\n"); int len = pos - DBGMSG.Length; logfile.write(rx.Substring(DBGMSG.Length, len)); rx = rx.Substring(pos+2); } } return rx; }
public bool SendHID(UInt32 cmdcode, byte [] data, int len) { bool sendOK = false; byte[] Report = new byte[usb.SpecifiedDevice.OutputReportLength]; int idx = 0; Report[idx++] = 0; //report ID //Добавление передаваемых данных в Report (полезная нагрузка): Report[idx++] = 'D'; Report[idx++] = 'E'; Report[idx++] = 'A'; Report[idx++] = 'D'; Report[idx++] = 'B'; Report[idx++] = 'E'; Report[idx++] = 'E'; Report[idx++] = 'F'; while (idx < usb.SpecifiedDevice.OutputReportLength) Report[idx++] = 0; //остаток буфера обнуляем try { usb.SpecifiedDevice.SendData(Report); sendOK = true; } catch (Exception ex) { logfile.write(ex.Message); } return sendOK; } } }
[Ссылки]
1. invoke in other class site:codeproject.com.
2. Visual Studio C++ 2010 Express: BackgroundWorker Class.
3. Идеология C#: события и делегаты.
System.Windows.Forms.Control.Invoke(System.Delegate)
Here are the examples of the csharp api class System.Windows.Forms.Control.Invoke(System.Delegate) taken from open source projects. By voting up you can indicate which examples are most useful and appropriate.
200 Examples
1. Example
Project: naps2
Source File: WiaBackgroundEventLoop.cs
public void Dispose() { if (thread != null) { try { form.Invoke(new Action(Application.ExitThread)); } catch (Exception ex) { Log.ErrorException("Error disposing WIA event loop", ex); } thread = null; } }
2. Example
Project: naps2
Source File: FormBase.cs
public void Invoke(Action action) { ((Control) this).Invoke(action); }
3. Example
Project: tcp-moe
Source File: Authentication.cs
public void Unlock() { this.Invoke((MethodInvoker)delegate { this.Enabled = true; }); }
4. Example
Project: tcp-moe
Source File: Loader.cs
public void InjectionFinished() { this.Invoke((MethodInvoker)delegate { UI.MsgBox.Show("Successfully loaded " + lstCheatList.SelectedItem.ToString() + "!", "Injection successful", MessageBoxIcon.Information); Worker.instance.Shutdown(); }); }
5. Example
Project: ClientServerProject
Source File: NetClientItem.cs
private void ThreadPoolLoadPortrait(object obj) { // ????????? if (m_NetAccount != null) { Bitmap bitmap = UserClient.PortraitManager.GetSmallPortraitByUserName(m_NetAccount.UserName); if(IsHandleCreated) { Invoke(new Action(() => { pictureBox1.Image = bitmap; })); } } }
6. Example
Project: ClientServerProject
Source File: FormMainWindow.cs
private void Net_socket_client_LoginSuccess() { // ?????????????????????????????? if (IsHandleCreated) Invoke(new Action(() => { toolStripStatusLabel_status.Text = "???????"; })); }
7. Example
Project: ClientServerProject
Source File: FormMainWindow.cs
private void Net_socket_client_BeforReConnected() { // ??????????????????????? if (IsHandleCreated && IsWindowShow) Invoke(new Action(() => { netClientOnline1.ClearOnlineClients(); })); }
8. Example
Project: ClientServerProject
Source File: FormMainWindow.cs
private void Net_socket_client_MessageAlerts(string object1) { // ???? if (IsHandleCreated) Invoke(new Action(() => { toolStripStatusLabel_status.Text = object1; })); }
9. Example
Project: BuildToolsGUI
Source File: BuildTools.cs
private void Disable() { if (InvokeRequired) { Invoke(_disableDelegate); } else { updateBT.Enabled = false; runBT.Enabled = false; versionBox.Enabled = false; } }
10. Example
Project: BuildToolsGUI
Source File: BuildTools.cs
private void Enable() { if (InvokeRequired) { Invoke(_enableDelegate); } else { updateBT.Enabled = true; runBT.Enabled = true; versionBox.Enabled = true; } }
11. Example
Project: BuildToolsGUI
Source File: BuildTools.cs
public void ProgressShow() { if (InvokeRequired) { Invoke(_showProgressDelegate); } else { progress.Visible = true; } }
12. Example
Project: BuildToolsGUI
Source File: BuildTools.cs
public void ProgressHide() { if (InvokeRequired) { Invoke(_hideProgressDelegate); } else { progress.Visible = false; } }
13. Example
Project: BuildToolsGUI
Source File: BuildTools.cs
public void ProgressIndeterminate() { if (InvokeRequired) { Invoke(_indeterminateProgressDelegate); } else { progress.Style = ProgressBarStyle.Marquee; } }
14. Example
Project: Depressurizer
Source File: CancelableDlg.cs
protected void DisableAbort() { if (InvokeRequired) { Invoke(new SimpleDelegate(DisableAbort)); } else { cmdStop.Enabled = cmdCancel.Enabled = false; } }
15. Example
Project: Depressurizer
Source File: MainForm.cs
private void reorderFillerColumn() { if (lstGames.InvokeRequired) { RemoveItemCallback callback = reorderFillerColumn; Invoke(callback); } else { // filler column should always be last column colFiller.DisplayIndex = (lstGames.ColumnsInDisplayOrder.Count - 1); } }
16. Example
Project: DroppedBoxx
Source File: Form1.cs
public void ShowSyncStatus() { if (this.InvokeRequired) { ShowSyncStatusDelegate dlg = new ShowSyncStatusDelegate(this.ShowSyncStatus); this.Invoke(dlg); return; } //do something with the GUI control here if (FoldersPanel.Visible) { //update the sync status stuff... FoldersPanel.UpdateSyncStatus(); } }
17. Example
Project: DotNetSiemensPLCToolBoxLibrary
Source File: ConnectionEditor.cs
public void enableCmdTest() { if (InvokeRequired) { Invoke(new MethodInvoker(enableCmdTest)); return; } cmdTest.Enabled = true; }
18. Example
Project: DotNetSiemensPLCToolBoxLibrary
Source File: Form1.cs
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) { try { myConn.Connect(); myDelegate += new stateConnectedDelegate(stateConnected); this.Invoke(myDelegate); } catch (Exception) { backgroundWorker1.ReportProgress(0); } }
19. Example
Project: WsdlUI
Source File: uc_Wm.cs
void uc_wm_request1_OnXmlFormatError(object sender, uc_WmRequest.XmlFormatErrorEventArgs e) { Invoke((MethodInvoker)(() => { uc_log1.LogErrorMessage(e.ErrorMessage); })); }
20. Example
Project: WsdlUI
Source File: uc_Wm.cs
void SetReady(string message) { Invoke((MethodInvoker)(() => { uc_log1.LogInfoMessage(message); uc_Status1.StatusReady(); uc_wm_request1.Enable(); tsbtn_Go.Enabled = true; tsbtn_Cancel.Enabled = false; })); }
21. Example
Project: WsdlUI
Source File: uc_Wm.cs
void SetInProgress() { Invoke((MethodInvoker)(() => { uc_log1.LogInfoMessage("request start"); uc_Status1.StatusInProgress(); uc_wm_request1.Disable(); tsbtn_Go.Enabled = false; tsbtn_Cancel.Enabled = true; })); }
22. Example
Project: duplicati
Source File: WinFormsRunner.cs
protected override void UpdateUIState(Action action) { m_handleProvider.Invoke(action); }
23. Example
Project: FOCA
Source File: PanelMetadataSearch.cs
public void HandleChangeStateEvent(object sender, EventsThreads.ThreadStringEventArgs e) { Invoke(new MethodInvoker(() => { if (Program.FormMainInstance != null) Program.FormMainInstance.toolStripStatusLabelLeft.Text = e.Message; Program.LogThis(new Log(Log.ModuleType.MetadataSearch, e.Message, Log.LogType.debug)); })); }
24. Example
Project: FOCA
Source File: PanelMetadataSearch.cs
private void HandleCustomSearchStartEvent(object sender, EventArgs e) { Invoke(new MethodInvoker(() => { Program.FormMainInstance.programState = FormMain.ProgramState.Searching; btnSearch.Text = "&Stop"; btnSearch.Image = Resources.magnifier_stop; })); }
25. Example
Project: FOCA
Source File: PanelMetadataSearch.cs
public void HandleCustomSearchEndEvent(object sender, EventsThreads.ThreadEndEventArgs e) { Invoke(new MethodInvoker(() => { Program.FormMainInstance.programState = FormMain.ProgramState.Normal; btnSearch.Text = "&Search"; btnSearch.Image = Resources.magnifier; })); }
26. Example
Project: FOCA
Source File: PanelMetadataSearch.cs
public void HandleSearchStartEvent(object sender, EventArgs e) { Invoke(new MethodInvoker(() => { btnSearchAll.Text = "&Stop"; btnSearchAll.Image = Resources.world_search_stop; checkedListBoxExtensions.Enabled = panelSearchConfiguration.Enabled = false; })); }
27. Example
Project: FOCA
Source File: PanelMetadataSearch.cs
public void HandleSearchEndEvent(object sender, EventsThreads.ThreadEndEventArgs e) { Invoke(new MethodInvoker(() => { btnSearchAll.Text = "&Search All"; btnSearchAll.Image = Resources.world_search; checkedListBoxExtensions.Enabled = panelSearchConfiguration.Enabled = true; })); }
28. Example
Project: FOCA
Source File: PanelMetadataSearch.cs
private void DownloadProgressChanged(object o, DownloadProgressChangedEventArgs dpce) { Invoke(new MethodInvoker(() => { ((Download) dpce.UserState).Pbar.Value = dpce.ProgressPercentage; ((Download) dpce.UserState).DownloadStatus = Download.Status.Inprogress; })); }
29. Example
Project: FOCA
Source File: PanelTasks.cs
public void AddNewTask(TaskFOCA t) { Invoke(new MethodInvoker(delegate { pendientes.BeginUpdate(); pendientes.Items.Add(t.id.ToString(), "[" + t.id + "] " + t, 75); pendientes.EndUpdate(); pendientes.Columns[0].Text = @"Queued tasks (" + pendientes.Items.Count + @")"; if (!bAutoScroll) return; if (pendientes.Items.Count > 0) pendientes.EnsureVisible(pendientes.Items.Count - 1); })); }
30. Example
Project: FOCA
Source File: PanelTasks.cs
public void StartTask(TaskFOCA t) { Invoke(new MethodInvoker(delegate { ejecucion.BeginUpdate(); ejecucion.Items.Add(t.id.ToString(), "[" + t.id + "] " + t, 75); ejecucion.EndUpdate(); pendientes.BeginUpdate(); pendientes.Items.RemoveByKey(t.id.ToString()); pendientes.EndUpdate(); ejecucion.Columns[0].Text = @"Running tasks (" + ejecucion.Items.Count + @")"; pendientes.Columns[0].Text = @"Queued tasks (" + pendientes.Items.Count + @")"; if (!bAutoScroll) return; if (ejecucion.Items.Count > 0) ejecucion.EnsureVisible(ejecucion.Items.Count - 1); })); }
31. Example
Project: FOCA
Source File: PanelTasks.cs
public void EndTask(TaskFOCA t) { Invoke(new MethodInvoker(delegate { realizadas.BeginUpdate(); realizadas.Items.Add(t.id.ToString(), "[" + t.id + "] " + t, 75); realizadas.EndUpdate(); ejecucion.BeginUpdate(); ejecucion.Items.RemoveByKey(t.id.ToString()); ejecucion.EndUpdate(); realizadas.Columns[0].Text = @"Finished tasks (" + realizadas.Items.Count + ")"; ejecucion.Columns[0].Text = @"Running tasks (" + ejecucion.Items.Count + ")"; if (!bAutoScroll) return; if (realizadas.Items.Count > 0) realizadas.EnsureVisible(realizadas.Items.Count - 1); })); }
32. Example
Project: BehaviorIsManaged
Source File: RichTextBoxTarget.cs
protected override void CloseTarget() { if (this.CreatedForm) { this.TargetForm.Invoke((FormCloseDelegate)this.TargetForm.Close); this.TargetForm = null; } }
33. Example
Project: vsSolutionBuildEvent
Source File: StatusFrm.cs
protected void push(RichTextBox box, string message) { // box.InvokeRequired may does not check properly try { box.Text += message; } catch { box.Invoke((MethodInvoker)delegate { box.Text += message; }); } }
34. Example
Project: nemiro.oauth.dll
Source File: MainForm.cs
private void RequestEnd() { if (this.InvokeRequired) { this.Invoke(new Action(RequestEnd)); return; } dataGridView1.Enabled = toolStrip1.Enabled = flowLayoutPanel1.Enabled = true; pictureBox1.Visible = false; }
35. Example
Project: FlingOS
Source File: MainForm.cs
private void UpdateRegisters() { if (InvokeRequired) { /n ..... /n //View Source file for more details /n }
36. Example
Project: bjd5
Source File: View.cs
public void SetColumnText(){ if (ListView == null) return; //Ver5.8.6 Java fix if (ListView.Columns.Count != 8){ return; } if (ListView.InvokeRequired){ ListView.Invoke(new MethodInvoker(SetColumnText)); } else{ //????????????????? ListView.Columns[0].Text = (_kernel.IsJp()) ? "??" : "DateTime"; ListView.Columns[1].Text = (_kernel.IsJp()) ? "??" : "Kind"; ListView.Columns[2].Text = (_kernel.IsJp()) ? "????ID" : "Thread ID"; ListView.Columns[3].Text = (_kernel.IsJp()) ? "??(???)" : "Function(Server)"; ListView.Columns[4].Text = (_kernel.IsJp()) ? "????" : "Address"; ListView.Columns[5].Text = (_kernel.IsJp()) ? "?????ID" : "Message ID"; ListView.Columns[6].Text = (_kernel.IsJp()) ? "??" : "Explanation"; ListView.Columns[7].Text = (_kernel.IsJp()) ? "????" : "Detailed information"; } }
37. Example
Project: trizbort
Source File: Canvas.Automap.cs
public Room FindRoom(string roomName, string roomDescription, RoomMatcher matcher) { Room room = null; try { m_control.Invoke((MethodInvoker)delegate() { room = m_canvas.FindRoom(roomName, roomDescription, matcher); }); } catch (Exception) { } return room; }
38. Example
Project: trizbort
Source File: Canvas.Automap.cs
public Room CreateRoom(Room existing, string name) { Room room = null; try { m_control.Invoke((MethodInvoker)delegate() { room = m_canvas.CreateRoom(existing, name); }); } catch (Exception) { } return room; }
39. Example
Project: trizbort
Source File: Canvas.Automap.cs
public Room CreateRoom(Room existing, AutomapDirection directionFromExisting, string name) { Room room = null; try { m_control.Invoke((MethodInvoker)delegate() { room = m_canvas.CreateRoom(existing, directionFromExisting, name); }); } catch (Exception) { } return room; }
40. Example
Project: trizbort
Source File: Canvas.Automap.cs
public void Connect(Room source, AutomapDirection directionFromSource, Room target) { try { m_control.Invoke((MethodInvoker)delegate() { m_canvas.Connect(source, directionFromSource, target); }); } catch (Exception) { } }
41. Example
Project: trizbort
Source File: Canvas.Automap.cs
public void AddExitStub(Room room, AutomapDirection direction) { try { m_control.Invoke((MethodInvoker)delegate() { m_canvas.AddExitStub(room, direction); }); } catch (Exception) { } }
42. Example
Project: trizbort
Source File: Canvas.Automap.cs
public void RemoveExitStub(Room room, AutomapDirection direction) { try { m_control.Invoke((MethodInvoker)delegate() { m_canvas.RemoveExitStub(room, direction); }); } catch (Exception) { } }
43. Example
Project: trizbort
Source File: Canvas.Automap.cs
public void SelectRoom(Room room) { try { m_control.Invoke((MethodInvoker)delegate() { m_canvas.SelectRoom(room); }); } catch (Exception) { } }
44. Example
Project: Baka-MPlayer-old
Source File: PlaylistControl.cs
private void OpenFile(string url) { Invoke((MethodInvoker)(() => mp.OpenFile(url))); }
45. Example
Project: Baka-MPlayer-old
Source File: MainForm.cs
private void mp_StdOutEvent(object sender, StdOutEventArgs e) { Invoke((MethodInvoker)delegate { if (e.StdOut.Equals("[ACTION] CLEAR_OUTPUT")) { outputTextbox.Clear(); return; } outputTextbox.AppendText("\n" + e.StdOut); // auto scroll to end outputTextbox.SelectionStart = outputTextbox.TextLength; outputTextbox.ScrollToCaret(); }); }
46. Example
Project: Baka-MPlayer-old
Source File: MainForm.cs
public void CallSetBackForwardControls() { Invoke((MethodInvoker)SetBackForwardControls); }
47. Example
Project: ArnoldSimulator
Source File: WinFormsExtensions.cs
public static void Invoke(this Control control, Action action) { control.Invoke(action); }
48. Example
Project: ArnoldSimulator
Source File: MainForm.cs
private void SimulationOnStateChanged(object sender, StateChangedEventArgs stateChangedEventArgs) { if (!IsDisposed) Invoke((MethodInvoker)UpdateButtons); }
49. Example
Project: TeraDamageMeter
Source File: DamageMeterForm.cs
private void InvokeAction(Action action) { if (IsDisposed) return; if (!InvokeRequired) throw new InvalidOperationException("Expected InvokeRequired"); Invoke(action); }
50. Example
Project: TeraDamageMeter
Source File: SnifferForm.cs
private void InvokeAction(Action action) { if (IsDisposed) return; if (!InvokeRequired) throw new InvalidOperationException("Expected InvokeRequired"); Invoke(action); }
Executes the specified delegate on the thread that owns the control’s underlying window handle.
Syntax
Parameters
- method
- A delegate that contains a method to be called in the control’s thread context.
Returns
The return value from the delegate being invoked, or null if the delegate has no return value.
Remarks
Delegates are similar to function pointers in C or C++ languages. Delegates encapsulate a reference to a method inside a delegate object. The delegate object can then be passed to code that calls the referenced method, and the method to be invoked can be unknown at compile time. Unlike function pointers in C or C++, delegates are object-oriented, type-safe, and more secure.
The Control.Invoke(Delegate) method searches up the control’s parent chain until it finds a control or form that has a window handle if the current control’s underlying window handle does not exist yet. If no appropriate handle can be found, the Control.Invoke(Delegate) method will throw an exception. Exceptions that are raised during the call will be propagated back to the caller.
The delegate can be an instance of EventHandler, in which case the sender parameter will contain this control, and the event parameter will contain EventArgs.Empty. The delegate can also be an instance of System.Windows.Forms.MethodInvoker, or any other delegate that takes a void parameter list. A call to an EventHandler or System.Windows.Forms.MethodInvoker delegate will be faster than a call to another type of delegate.
Note:
An exception might be thrown if the thread that should process the message is no longer active.
Requirements
Namespace: System.Windows.Forms
Assembly: System.Windows.Forms (in System.Windows.Forms.dll)
Assembly Versions: 1.0.5000.0, 2.0.0.0
До сих пор мы работали с консолью не просто так. Дело в том, что у потоков есть множество ограничений. Далеко не все объекты .NET также безопасно могут существовать при обращении к ним со стороны нескольких потоков. Давайте посмотрим это на примере. Создайте новое WinForms-приложение и поместите на форму компонент RichTextBox и кнопку. Пишем код, который должен выполняться по нажатию кнопки:
private void button1_Click(object sender, EventArgs e) { Thread thread = new Thread(new ThreadStart(ThreadFunction)); thread.Start(); }
Здесь у нас создается и запускается на выполнение новый поток, который будет выполнять метод ThreadFunction(). Метод потока выглядит следующим образом:
void ThreadFunction() { for (int i = 0; i < 10; i++) richTextBox1.AppendText(i.ToString()); }
Здесь мы в цикле из десяти шагов пытаемся вывести число в компонент RichTextBox. На самом деле у нас не выйдет даже одного раза вывести что-то в этот компонент. Нет, мы сможем откомпилировать пример и даже запустить его на выполнение. Но при попытке нажать на кнопку произойдет исключительная ситуация. На рис. 1 показано сообщение об исключительной ситуации из среды разра- ботки.
Рис. 1. Сообщение об исключительной ситуации
Нельзя обращаться к элементам управления, которые были созданы в других потоках. Наш элемент управления на форме был сделан в основном потоке, а доступ мы пытаемся получить из дочернего потока. Так как же нам получить доступ к компоненту из потока? Эта задача достаточно популярна при работе с потоками и решается довольно легко — через делегаты.
Для вывода текста в RichTextBox напишем следующий метод:
void PrintFunc(string str) { richTextBox1.AppendText(str); }
Метод выводит в компонент переданный в качестве параметра текст. Чтобы вызвать этот метод, нужно объявить соответствующий делегат и переменную делегата:
delegate void PrintInRhichTextBox(string str); private PrintInRhichTextBox PrintDelegateFunc;
В конструкторе инициализируем переменную делегата:
PrintDelegateFunc = new PrintInRhichTextBox(PrintFunc);
Теперь можно вызывать этот метод через делегат. Но не просто так, а через метод Invoke() компонента:
richTextBox1.Invoke(PrintDelegateFunc, new object[] { i.ToString() });
Методу Invoke() передаются два параметра:
- делегат, который нужно выполнить;
- массив объектов параметров.
В нашем случае методу делегата нужно передать только один параметр — строку, которая должна выводиться в компоненте, ее и передаем.
Метод Invoke() компонента вызывает указанный метод делегата в потоке, которому и принадлежит этот компонент. Метод Invoke() возвращает значение, которое возвращает вызываемый делегат, или null, если делегат не возвращает никаких значений.
Вот и все. Немного сложновато, но эффективно. При таком вызове делегата он будет работать в том потоке, в котором был создан компонент, а значит, исключительной ситуации не возникнет.
Внимание!!! Если ты копируешь эту статью себе на сайт, то оставляй ссылку непосредственно на эту страницу. Спасибо за понимание