IT rekvalifikace s garancí práce. Seniorní programátoři vydělávají až 160 000 Kč/měsíc a rekvalifikace je prvním krokem. Zjisti, jak na to!
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

Diskuze: c# form - chyba: Nelze volat funkci Invoke...

Aktivity
Avatar
Michaal.K
Člen
Avatar
Michaal.K:28.3.2016 23:16

Ahoj,
objevil se mi problém a program hází chybu:

Nelze volat funkci Invoke nebo BeginInvoke pro ovládací prvek, dokud není vytvořen popisovač okna.

A nevím co stím. Děje se to v tříde settingsform(je to form s nastavením), když formulář otevřu podruhé a stisknu tlač. Test připojení (metoda buttonTest_Click). Chyba nastane v comm_OnTimedEvent. Tlačítko je vytvořeno v kódu třídy settingsform v metodě load. Zkrácený kód třídy settingsform:

Communication comm = Communication.Instance;

      public SettingsForm(MainForm form)
      {
         InitializeComponent();

            this.FormBorderStyle = FormBorderStyle.FixedDialog;             // Zakaz zmeny velikosti

            comm.OnSerialDataReceived += new Communication.DataReceivedEventHandler(comm_OnSerialDataReceived);
            comm.OnTimedEvent +=new Communication.TimedEventEventHandler(comm_OnTimedEvent);
      }

        void buttonTest_Click(object sender, EventArgs e)
        {
            Button buttonAkt = (Button)sender;                              // Zjisteni na ktery button se kliklo
            var tabConParent = buttonAkt.Parent.Parent;                     // Zjisteni rodice(zalozka tabControl) buttonu na ktery se kliklo

            List<ComboBox> allCombos = new List<ComboBox>();                // Ulozeni vsech comboboxu
            allCombos.AddRange(GetComboBoxes((Control)tabConParent));       // Ulozi vsechny comboboxy v dane zalozce tabControl

            comm.serialPorts[tabConParent.TabIndex].BaudRate = Convert.ToInt32(allCombos[0].SelectedItem);                  // Do listu serialPorts ulozim aktualne nastavene hodnoty z comboBoxu
            comm.serialPorts[tabConParent.TabIndex].Parity = (Parity)Enum.Parse(typeof(Parity), allCombos[1].Text);
            comm.serialPorts[tabConParent.TabIndex].StopBits = (StopBits)Enum.Parse(typeof(StopBits), allCombos[2].Text);
            comm.ConnectionTest(comm.serialPorts[tabConParent.TabIndex]);   // Zavolam test pripojeni
        }

        void comm_OnSerialDataReceived(SerialPort comPort, string data)
        {
            this.Invoke(new EventHandler(delegate
            {
                Form frm = Application.OpenForms["SettingsForm"];
                if (frm == null)                                                            // Zjistim jestli je aktivni SettingsForm pokud ano pokracuji dal
                    return;                                                                 // Ukoncim metodu pokud neni aktivni form, abych nezapisoval do data do prvku ktery neni vytvoren!!

                int indexPortu = comm.serialPorts.IndexOf(comPort);                         // Index portu v tabControl a listu serialPorts
                var tabConParent = tabControl1.TabPages[indexPortu];                        // Zalozka tabPages kde bylo stisknuty button Test pripojeni
                var gbTestAkt = tabConParent.GetContainerControl().ActiveControl.Parent;    // Aktualni groupBox v dane zalozce tabPages

                TextBox textBoxAkt = new TextBox();                                         // Ulozeni TextBoxu
                textBoxAkt = GetTextBox((Control)gbTestAkt);                                // Ulozi TextBox v dane zalozce tabControl a groubBoxu

                textBoxAkt.Font = new Font("Microsoft Sans Serif", 8);
                textBoxAkt.Text = data;                                                     // Zapsani prijatych dat
                textBoxAkt.TextAlign = HorizontalAlignment.Center;                          // Zarovnani textu na stred
                textBoxAkt.BackColor = Color.GreenYellow;
            }));
        }

        // Vyvola se tato metoda pokud dojde k preteceni casovace -> Timeout komunikace
        void comm_OnTimedEvent()
        {
            this.Invoke(new EventHandler(delegate
            {
                int indexTabPage = tabControl1.SelectedIndex;                               // Index aktualni zalozkt v tabControl a listu serialPorts
                var tabConParent = tabControl1.TabPages[indexTabPage];                      // Zalozka tabPages kde bylo stisknuty button Test pripojeni
                var gbTestAkt = tabConParent.GetContainerControl().ActiveControl.Parent;    // Aktualni groupBox v dane zalozce tabPages

                TextBox textBoxAkt = new TextBox();                                         // Ulozeni TextBoxu
                textBoxAkt = GetTextBox((Control)gbTestAkt);                                // Ulozi TextBox v dane zalozce tabControl a groubBoxu

                textBoxAkt.Font = new Font("Microsoft Sans Serif", 8, FontStyle.Bold);
                textBoxAkt.Text = "Časový limit operace vypršel!";
                textBoxAkt.TextAlign = HorizontalAlignment.Center;
                textBoxAkt.BackColor = Color.DarkRed;
            }));
        }

a tady je zkrácený kód třídy communication, kde je nastavení a spuštění timeru:

public delegate void TimedEventEventHandler();                                      // Deklarace delagata, ktery bude slouzit jako predpis metody predstavujici reakci na udalost (handler)
        public event TimedEventEventHandler OnTimedEvent;                                   // Deklarace udalosti

        private System.Timers.Timer timerTimeout;


// Metoda, ktera provede nastaveni timeru pro timeout komunikace
        private void SetTimerTimeout()
        {
            timerTimeout = new System.Timers.Timer(500);
            timerTimeout.Elapsed += new System.Timers.ElapsedEventHandler(timerTimeout_Elapsed);        // Pridani udalosti, ktera je vyvolana pokud dojde k preteceni casovace
        }

        // Metoda, ktera se vykona pri preteceni casovace
        void timerTimeout_Elapsed(object sender, EventArgs e)
        {
            System.Timers.Timer timerTimeout = (System.Timers.Timer)sender;
            timerTimeout.Enabled = false;                       // Zastavim timer

            Form frm = Application.OpenForms["SettingsForm"];
            if (frm != null)                                    // Zjistim jestli je aktivni SettingsForm pokud ano zavolam metodu
                OnTimedEvent();
            else
                MessageBox.Show("Časový limit operace vypršel!", MainForm.appName, MessageBoxButtons.OK, MessageBoxIcon.Error);

            Disconnect();                                       // Zavru vsechny porty
        }

        public void ConnectionTest(SerialPort comPort)
        {
            SetTimerTimeout();                                  // Nastaveni parametru timeru
            ConnectedPort(comPort);                             // Otevru dany seriovy port
            int adrPortuList = serialPorts.IndexOf(comPort);    // Zjistim index daneho serioveho portu v listu serialPorts
            WriteData(adrPortuList, "Test");                    // Vyslu testovaci retezec
            timerTimeout.Enabled = true;                        // Zapnu timer
        }

Díky za pomoc a rady...

 
Odpovědět
28.3.2016 23:16
Avatar
VitekST
Člen
Avatar
VitekST:29.3.2016 16:58

No heleme se, nejdříve na fóru Živě, teď i tady na ITnetworku.
Dobrá, nebudu jim dělat reklamu, koneckonců, zvýrazňování syntaxe je zde mnohem lepší. :)

Jak jsem říkal, není dobrý nápad se připichovat na událost, která využívá Invoke/BeginInvoke, když není vytvořen popisovač okna.
Totiž toto se tady děje.

Potřebuješ se připíchnout na událost jakmile formulář je zobrazen, aby si nezavolal metodu Invoke/BeginInvoke když popisovač neexistuje.

Překryj si metodu "SettingsForm­.OnShown" (připíchnutí na událost z nějakýho důvodu nefunguje), a připíchni si události z timeru tam.

protected override void OnShown(EventArgs e)
{
        //Připichujeme se na události
        comm.OnSerialDataReceived += new Communication.DataReceivedEventHandler(comm_OnSerialDataReceived);
        comm.OnTimedEvent +=new Communication.TimedEventEventHandler(comm_OnTimedEvent);

        //Voláme původní metodu (tu překrytou)
        base.OnShown(e);
}
 
Nahoru Odpovědět
29.3.2016 16:58
Avatar
Odpovídá na Michaal.K
sadlomaslox25:29.3.2016 19:23

chyba je v tom ze mas staticky event a vznikani ti tak "memory leak". musis se v metode on form close/hide odregistrovat
comm.OnSerial­DataReceived -= new Communication­.DataReceivedE­ventHandler(com­m_OnSerialData­Received);
comm.OnTimedEvent -=new Communication­.TimedEventEven­tHandler(comm_On­TimedEvent);
}

proto ti to spadne az pri druhem formulari protoze ten event posila data tomu prvnimu formulari ktery ale uz davno neexistuje ale ten event si ho furt pamatuje.

Akceptované řešení
+20 Zkušeností
+2,50 Kč
Řešení problému
 
Nahoru Odpovědět
29.3.2016 19:23
Avatar
Michaal.K
Člen
Avatar
Odpovídá na sadlomaslox25
Michaal.K:30.3.2016 14:58

Ahoj. Díky moc...
Vytovořil jsem metodu formclose a tam jsem odregistroval obě události. A teď už to funguje.
Ještě jednou díky...

 
Nahoru Odpovědět
30.3.2016 14:58
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 4 zpráv z 4.