W tym poście chciałbym krótko wyjaśnić w jaki sposób obsługiwać kontrolki z innego wątku.
[Źródło: https://docs.microsoft.com/en-us/dotnet/]
Często zdarza się, że np. zachodzi potrzeba wykorzystania klasy BackgroundWorker, czy stworzenie nowego wątku Thread z których trzeba zaktualizować dane udostępnione w kontrolkach na ekranie.
Podczas tworzenia kontrolek w przygotowywanej aplikacji są one dodawane do wątku głównego. Gdy zostaje stworzony osobny wątek to nie można bezpośrednio odnieść się do kontrolki stworzonej w innym wątku. Spowoduje to wygenerowanie następującego błędu:
W przykładzie wykorzystuje klasę BackgroundWorker do pobierania ustawień przesłanych przez TCP Klient do TCP Serwer. TCP Klient jest to przygotowywana aplikacja w C#. TCP Serwer jest układ Arduino Nano wraz z modułem ENC28J60. Program przesyła i ustawia parametry sieciowe oraz kilka dodatkowych ustawień.
Jeśli chcemy wykorzystywać jedną funkcję aktualizującą dane zarówno wątku głównym jak i pobocznym to można wykonać sprawdzanie który wątek wywołał daną funkcję.
Dostęp do kontrolek z poziomu funkcji wykonywanej przez klasę BackgroundWorker wygląda następująco:
Poniżej wykonałem wpisanie wartości za pomocą kilki metod:
- newIpTxtBox.Dispatcher.Invoke(() => { newIpTxtBox.Text = ipValue; });
- newMaskTxtBox.Dispatcher.BeginInvoke(new Action(() => { newMaskTxtBox.Text = maskValue; }));
- newGateTxtBox.Dispatcher.Invoke(delegate { newGateTxtBox.Text = gateValue; });
- newPortTxtBox.Dispatcher.Invoke(() => { newPortTxtBox.Text = portValue; });
- DHCPEnableCheckBox.Dispatcher.Invoke(() => { DHCPEnableCheckBox.IsChecked = Convert.ToBoolean(dhcpValue); });
- alarmLengthTxtBox.Dispatcher.Invoke(() => { alarmLengthTxtBox.Text = alarmTimeValue; });
- askTimeTxtBox.Dispatcher.Invoke(() => { askTimeTxtBox.Text = askingTimeValue; });
- textBox1.Dispatcher.Invoke(new Action(delegate () { textBox1.AppendText("---" + "\n"); }));
Jak można zauważyć powyżej wykorzystywana jest klasa Dispatcher. Przekazuje ona wykonanie zadania do wątku w którym dana kontrolka została stworzona.
Można tu wyróżnić dwie funkcje Invoke oraz BeginInvoke. Pierwsza z nich pozwala na synchroniczne wykonanie zadania. Druga natomiast pozwala na wykonanie danego delegata asynchronicznie.
W przypadku korzystania z WindowsForm można wykorzystać np. taką instrukcję:
Można tu wyróżnić dwie funkcje Invoke oraz BeginInvoke. Pierwsza z nich pozwala na synchroniczne wykonanie zadania. Druga natomiast pozwala na wykonanie danego delegata asynchronicznie.
W przypadku korzystania z WindowsForm można wykorzystać np. taką instrukcję:
- textBox1.Invoke(new Action(delegate () { textBox1.AppendText("---" + "\n"); }));