C# Odwołanie w klasie do listbox'a z innej klasy

0

Cześć,
Od kilku dni uczę się programowania C# i napotkałem problem, na który nie potrafię znaleźć rozwiązania. Przeszukałem już trochę Internetu, ale stosując odnalezione tam porady nie udało mi się go rozwiązać, więc proszę Was o poradę bo może robię coś źle, albo w ogóle cała logika tego programu jest źle skontruowana..
Mianowicie, próbuję napisać prosty komunikator TCP, korzystam przy tym z wątków i całość można powiedzieć działa, ale nie potrafię w listbox'ie wyświetlić odebranej wiadomości. W klasie 'ConnectionThread' nie mogę się odwołać do listbox_status.. z klasy 'Form1'. Próbowałem też utworzyć nowy obiekt klasy 'Form1' w klasie 'ConnectionThread', jednak mimo że mogę się wtedy odwołać się do tego listbox'a, nic się w nim nie wyświetla.
Poniżej kod programu - zbędne rzeczy wyciąłem, w komentarzu zaznaczyłem co i gdzie chcę zrobić, proszę o jakieś sugestie bo już naprawdę nie wiem co jest tutaj źle..
Z góry, dzięki wielkie za pomoc :)

namespace proc_i_watki_serwer_TCP_komunik_
{
    delegate void del2(string string_ip);


    public partial class Form1 : Form
    {
        public static string ip_serwera;
        public static int port_nasluchu;


        public Form1()
        {
            InitializeComponent();
            Thread adresacja = new Thread(new ThreadStart(pobierz_ip));
            adresacja.Start();
        }


        public void pobierz_ip()
        {
            string string_ip;

            IPHostEntry host = Dns.GetHostEntry(Dns.GetHostName());
            foreach (IPAddress ip in host.AddressList)
            {
                if (ip.AddressFamily == AddressFamily.InterNetwork)
                {
                    string_ip = ip.ToString();
                    if (label_ip.InvokeRequired)
                    {
                        del2 g = new del2(SetText2);
                        Invoke(g, new object[] { string_ip });
                    }
                    else
                    {
                        label_ip.Text = "IP: " + string_ip;
                    }
                    ip_serwera = ip.ToString();
                }
            }
        }
        private void SetText2(string string_ip)
        {
            label_ip.Text = "IP: " + string_ip;
        }

		
        public void button_wlacz_Click(object sender, EventArgs e)
        {
            Thread nasluchiwanie = new Thread(new ThreadStart(server));

            
            int port_nas = (int)numeric_serwer_port.Value;
            port_nasluchu = port_nas;

            listBox_status.Items.Add(ip_serwera + ": " + port_nasluchu);
            nasluchiwanie.Start();
            }
            else
            { //...              
            }
    }


    public void server()
    {
        ThreadedTcpSrvr server = new ThreadedTcpSrvr();
    }



    public class ThreadedTcpSrvr
    {
        private TcpListener client;
        

        public ThreadedTcpSrvr()
        {
           
            client = new TcpListener(IPAddress.Parse(Form1.ip_serwera), Form1.port_nasluchu);
            client.Start();

            try
            {
                while (true)
                {
                    while (!client.Pending())
                    {
                        Thread.Sleep(1000);
                    }

                    ConnectionThread newconnection = new ConnectionThread();
                    newconnection.threadListener = this.client;
                    Thread newthread = new Thread(new ThreadStart(newconnection.HandleConnection));
                    newthread.Start();
                }
            }
            catch (ObjectDisposedException e)
            {
            }
        }
    }


    public class ConnectionThread
    {

        public TcpListener threadListener;
        public NetworkStream ns;
        public StreamReader sr;
        public TcpClient client;

        
        public void HandleConnection()
        {
            
            client = threadListener.AcceptTcpClient();

            ns = client.GetStream();
            sr = new StreamReader(ns);
            
            IPEndPoint clientep = (IPEndPoint)client.Client.RemoteEndPoint;

            Thread odbior = new Thread(new ThreadStart(odbieranie));
            odbior.Start();
        }


        public void odbieranie()
        {
            string responseData;
            
            while (true)
            {
                responseData = sr.ReadLine();

                if (responseData == null)
                    break;

				// TUTAJ CHCIAŁEM WYŚWIETLIĆ W LISTBOX'IE TO CO OTRZYMAŁEM OD INNEGO HOSTA

                //test.listBox_status.Items.Add(responseData);    - TAK SIĘ NIE DA
            }

            ns.Close();
            client.Close();
        }
    }
}
0
  1. Użyj Invoke
  2. Do listy bez problemu dodasz itema - jaki masz błąd bo tego nie napisałes
0

Fragmenty w kodzie:

  1. Tak się nie da zadeklarować bo mam error jak poniżej.
  2. Tworząc w klasie:
 public class ConnectionThread
            {//...
        public Form1 form = new Form1();
           //....

obiekt klasy Form1 program się kompiluje, ale nie widać efektów, czyli nie dopisuje otrzymanej wiadomości w listbox'ie

        public void odbieranie()
        {
            string responseData;
 
            while (true)
            {
                responseData = sr.ReadLine();
 
                if (responseData == null)
                    break;

              // --- 1 ------
              //listBox_status.Items.Add(responseData);    - TAK NIE MOGĘ SIĘ ODWOŁAĆ     Error:'The name 'listBox_status' does not exist in the current context'//
              
              // --- 2 ------
             form.listBox_status.Items.Add(responseData);    - TAK PROGRAM SIĘ URUCHAMIA, ALE W LISTBOX'IE KOMUNIKATY SIĘ NIE WYŚWIETLAJĄ

            }
 
            ns.Close();
            client.Close();
        } 

Z tego co wiem Invoke służy do tego, aby 2 wątki nie próbowały naraz modyfikować tego listbox'a? Czyli jeśli robię tak jak powyżej (jeszcze bez Invoke'a) to chyba powinno mi wyświetlić błąd w trakcie próby dodania tekstu do listbox'a..?

1

obiekt klasy Form1 program się kompiluje, ale nie widać efektów, czyli nie dopisuje otrzymanej wiadomości w listbox'ie

Ależ dopisuje, tylko nie tam gdzie ci się wydaje - przecież utworzyłeś nowy obiekt typu Form1, więc nową formę. Nie pokazujesz jej, więc jej nie widać - ale ona jest.

W ten sposób nie dostaniesz się do formy która istniała wcześniej - musisz przekazać jakoś istniejącą zmienną, np. w parametrze konstruktora.

0
Azarien napisał(a):

W ten sposób nie dostaniesz się do formy która istniała wcześniej - musisz przekazać jakoś istniejącą zmienną, np. w parametrze konstruktora.

A możesz pomóc jak to przekazanie zmiennej powinno wyglądać w tym przypadku? Bo kombinuję, ale cały czas jakieś błędy się pojawiają..

1
class Form1
{
    Form2 form2;
    public Form1()
    {
        form2 = new Form2(listbox1);
        form2.Show();
    }
    
    void AddListBoxItem()
    {
        form2.AddItem();
        //lub listboxa przekazujesz dopiero w metodzie więc możesz go wywalić z konstruktora Form2
       form2.AddItem(listbox1);
    }
    
}
class Form2
{
    ListBox listbox;
    public Form2(ListBox listbox)
    {
        this.listbox = listbox;
     }

   void AddItem()
   {
        this.listbox.Items.Add(blablabla);
    }

   void AddItem(Listbox listbox)
   {
         listbox.Items.Add(blablabla);
    }
}

Być może to nie jest dokładnie to czego potrzebujesz, ale powinno Cię naprowadzić, a nie chce mi się ogarniać twojego kodu.

0

Tworząc:

namespace proc_i_watki_serwer_TCP_komunik_
{    .....

    delegate void del4(string listbox);

    public partial class Form1 : Form
    {   .....

        public void AddItem(string listbox)
        {
            if (listBox_status.InvokeRequired)
            {
                     del4 d = new del4(SetText4);
                     Invoke(d, new object[] { listbox });
             }
             else
             {
                    listBox_status.Items.Add(listbox);
              }
        }
        private void SetText4(string listbox)
        {
            listBox_status.Items.Add(listbox);
        }
    }


    public class ConnectionThread
    {    .......

        public Form1 form; 
         .........


        public void odbieranie()
        {
            string responseData;
            form = new Form1();


            while (true)
            {
                responseData = sr.ReadLine();

                if (responseData == null)
                    break;

                form.listBox_status.Items.Add(responseData);  // TAKI SPOSÓB WYŚWIETLA
                form.ShowDialog();                         // NOWĄ FORMĘ GDZIE LISTBOX SIĘ WYPEŁNIA
                
                 form.AddItem(responseData);       // TYM SPOSOBEM BŁĄD SIĘ NIE POJAWIA ALE NA LISTBOX'IE w Form1 nic się nie wyświetla
                message = "Thank";
                sw.WriteLine(message);
                sw.Flush();
            }

            //........
        }       
      }
    }
} 

działa tak jak opisane powyżej. Już nie wiem jak to zrobić.. Proszę pomóżcie..

0

Zrozumiałeś cokolwiek z mojego kodu? Przekaż ten listbox w konstuktorze do klasy ConnectionThread.

1

tutaj

form = new Form1();

tworzysz zupełnie nową formę, która nie ma nic wspólnego z tym co widzisz na ekranie. Nie jest dodana do aplikacji.
Nie tak powinno się robić, ale najkrótsza droga to taka, żeby przekazać Form1 poszczególnym obiektom i tak. Konstruktor ThreadedTcpSrvr niech przyjmuje na parametr Form:

public ThreadedTcpSrvr(Form1 form)

podobnie ConnectionThread:

public ConnectionThread(Form1 form)
{
   this.form = form;
}

Pamiętaj o usunięciu linijki form = new Form1();
Dalej tworzenie obiektu ThreadedTcpSrvr:

ThreadedTcpSrvr server = new ThreadedTcpSrvr(this);

W konstruktorze jego wykorzystujesz przekazany parametr do utworzenia obiektu ConnectionThread

ConnectionThread newconnection = new ConnectionThread(form);

W tym momencie będziesz miał właściwy obiekt Form1 w klasie w której chcesz mieć.

Na marginesie masz 2x dodawanie itemu tutaj:

form.listBox_status.Items.Add(responseData);
form.ShowDialog();
form.AddItem(responseData);
0

Dzięki wielkie za pomoc, z Waszymi radami udało się to zrobić.
Odbieranie tekstu działa, teraz walczę z wysyłaniem i znowu topornie mi to idzie..
Po kliknięciu przycisku button_wyslij udaje mi się odczytać wiadomość wpisaną w textBox_wyslij i zapisać ją do zmiennej, ale przy próbie wysłania strumieniem pojawia mi się błąd 'Odwołanie do obiektu nie zostało ustawione na wystąpienie obiektu.'
Czy możecie spojrzeć co robię nie tak..?
Poniżej najważniejsze elementy kodu, w załączniku dałem cały.

 namespace proc_i_watki_serwer_TCP_komunik_
{
    delegate void del4(string listbox);
    delegate void del5(string listbox2);


    public partial class Form1 : Form
    { ...............

        public Form1()
        {
            InitializeComponent();
        }

        public void AddItem(string listbox)
        {
            ......  // Dodawanie stringów do listboxa
        }


        public void server()
        {
          ThreadedTcpSrvr server = new ThreadedTcpSrvr(this);     
        }
    }


    public class ThreadedTcpSrvr
    {
        private TcpListener client;

        public ThreadedTcpSrvr(Form1 form)
        {

            client = new TcpListener(IPAddress.Parse(Form1.ip_serwera), Form1.port_nasluchu);
            client.Start();
               
            while (true)
            {       
                while (!client.Pending())
                {
                    Thread.Sleep(1000);
                }

                ConnectionThread newconnection = new ConnectionThread(form);
                newconnection.threadListener = this.client;
                Thread newthread = new Thread(new ThreadStart(newconnection.HandleConnection));
                newthread.Start();
            }
        }
    }


    public class ConnectionThread
    {
        public TcpListener threadListener;
        public NetworkStream ns;
        public StreamReader sr;
        public StreamWriter sw;
        public TcpClient client;
        Form1 form;


        public ConnectionThread(Form1 form)
        {
            this.form = form;
        }

        public void HandleConnection()
        {
            form.button_wyslij.Click += new EventHandler(ClickedWyslij);
           
            client = threadListener.AcceptTcpClient();

            ns = client.GetStream();
            sr = new StreamReader(ns);
            sw = new StreamWriter(ns);

            IPEndPoint clientep = (IPEndPoint)client.Client.RemoteEndPoint;
            

            Thread odbior = new Thread(new ThreadStart(odbieranie));
            Thread wysyl = new Thread(new ThreadStart(wysylanie));
            odbior.Start();
            wysyl.Start();
        }


        public void odbieranie()
        {
            string responseData;

            while (true)
            {
                responseData = sr.ReadLine();
                form.AddItem("Klient nr: " + numer + " " + responseData);
            }
            ns.Close();
            client.Close();
        }


        public void ClickedWyslij(object sender, EventArgs e)
        {					// Sprawdzam czy naciśnięty przycisk form.button_wyslij
            wysylanie();	                // jeśli tak to wywołuję wysyłanie
        }

        public void wysylanie()
        {
            string input;

            input = this.form.textBox_wyslij.Text;	 // pobieram stringa z textbox_wyslij z klasy Form1
            MessageBox.Show(input);			 // wyświetlam komunikat dla sprawdzenia - wyświetla się prawidłowy tekst, ale okienko otwiera się 2 razy
            
            sw.WriteLine(input);			// przy tej linii pojawia się błąd 'NulleReferenceException'
            sw.Flush();				// 'Odwołanie do obiektu nie zostało ustawione na wystąpienie obiektu.'
        }
    }
}

Po naciśnięciu przycisku 'Włącz' uruchamiane jest nasłuchiwanie na porcie wskazanym obok przycisku 'Włącz' i adresie z labelu pod przyciskiem.
Jeśli przyjdzie połączenie od klienta tworzony jest wątek do obsługi z nim komunikacji.
W listbox_status pojawiają się odebrane wiadomości i komunikaty serwera, w listbox_rozmowcy adresy klientów.
Po wpisaniu tekstu w textbox_wyslij i naciśnięciu przycisku 'Wyślij' tekst powinien zostać wysłany do wszystkich klientów.
Tak w skrócie ma to działać.

0

Masakra.
Jeszcze powiedz jak to działa.

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