Dostęp do obiektu ListBox w wątku

0

Utworzyłem klasę obsługującą obsługę wątku:

using System;
using System.Windows.Forms;

public class HeadersThread
{
	private ListBox listEmailSubjects;
	public HeadersThread(ListBox listEmailSubjects)
	{
		this.listEmailSubjects = listEmailSubjects;
	}
	
	public void getHeaders()
	{
		ImapService iS = new ImapService();
		ServersSettings sSettings = new ServersSettings("conf.xml");
		sSettings.readSettings();
		iS.setServer(sSettings.getImapServer());
		iS.getServer().setPass("tajne hasło");
		if(iS.readMessages())
		{
			for(int i=iS.countEmailMessages()-1; i>=0; i--)
				listEmailSubjects.Items.Add(iS.getEmailMessage(i).getSubject());
		}
		else
			MessageBox.Show("Nie sprawdzono poczty.");
	}
}

Chodzi o to aby lista wiadomości email pobierała się w osobnym wątku i nie blokowała całego programu.
Gdy wywołuję wykonanie wątku w głównym programie:

HeadersThread ht = new HeadersThread(listEmailSubjects);
Thread t = new Thread(new ThreatStart(ht.ThreadFunc));
t.Start();

To robi co do niego należy, ale przy próbie wypełnienia listy pojawia się błąd:

Próba uzyskania dostępu do obiektu, który jest wykorzystywany przez inny wątek.

Jak rozwiązać ten problem?

0

Poczytaj o delegatach lub możesz użyć trochę na siłe backgroundworker.

0

Dzięki. Pewnie wygłupiłem się nieznajomością tak podstawowej rzeczy - fakt nie do końca zaczaiłem o co chodzi z delegatami. Poczytam więcej, bo mam wrażenie, że rzucam się na rzeczy, które mnie obecnie przerastają.

1

To wcale nie jest trudne tak bardzo jak się wydaje. ;) Najprościej mówiąc delegat to taka funkcja która umożliwia zmianę wartości obiektów stworzonych w jednym procesie przez inny proces, jest to w tym wypadku taki pośrednik. Tu masz przykład

        public delegate void updateListBoxDelegate(String info);
        private void updateListBox(String info)
        {
            if (InvokeRequired)
                listBox1.Invoke(new updateListBoxDelegate(this.updateListBox),
                new object[] {info});
            else
            {
                //Tutaj kod aktualizujący kontrolke
                listBox1.Items.add(info);
            }
        }

I ta funkcja musi być metodą klasy formy z tym listboxem natomiast w klasie osobnego wątku wywołujesz ją jak każdą inna metodę czyli updateListBox("test"); Oczywiście pewnie obiekt formy będziesz musiał przekazać do tej klasy wykonywanej w nowym wątku, żeby móc wywołać te metodę form1.updateListBox("test"). No tyle ci starczy

0

Dzięki, działa. Muszę jeszcze zrozumieć jak i dlaczego ;) Wszędzie wyczytałem, że delegaty to takie wskaźniki do funkcji, które znam z C++, ale o komunikacji międzyprocesowej nie było słowa.

2
  1. To jest komunikacja międzywątkowa a nie międzyprocesowa.
  2. Do kontrolek, form, itp. można się odwoływać tylko z wątku, w którym powstały (czyli zwykle wątku głównym). Jest to ograniczenie narzucane przez sam Windows i obowiązuje chyba we wszystkich bibliotekach do GUI.
  3. metoda Invoke powoduje wywołanie podanej metody (delegaty) w odpowiednim wątku. przykładowo, listBox1.Invoke wywoła podaną metodę z podanymi parametrami w wątku, który stworzył kontrolkę listBox1. dzieje się to tak:
  • wątek zawierający wywołanie Invoke jest wstrzymywany i czeka.
  • wątek główny dostaje do kolejki zdarzeń informację, że ma do wykonania metodę przez Invoke. zdarzenia z kolejki są obsługiwane w pętli. kiedy nadchodzi czas na zdarzenie z Invoke, wykonywana jest podana metoda z parametrami – w przykładzie wyżej updateListBox(info).
  • po zakończeniu tej metody wznawiany jest wątek który wywołał Invoke i Invoke kończy działanie, wracając do metody w której było wywołane.

Dodatkowo jest właściwość InvokeRequired. Zwraca ona false, jeśli bieżący wątek jest tym właściwym dla danej kontrolki. Czyli w głównym wątku zwróci false, a w pozostałych - true.

Przykład @mr_jaro używa sztuczki z tym związanej: wewnątrz metody updateListBox sprawdzane jest InvokeRequired, i jeśli jest prawdą to wykonuje Invoke na tej samej metodzie updateListBox. Metoda jest wykonywana jeszcze raz od początku - ale tym razem w głównym wątku, tym razem InvokeRequired zwraca false, i wykonuje się kod spod else.

1 użytkowników online, w tym zalogowanych: 0, gości: 1