Diskuze: C# - předávání dat mezi třídou a formulářem

C# .NET .NET (C# a Visual Basic) C# - předávání dat mezi třídou a formulářem American English version English version

Aktivity (3)
Avatar
Michaal.K
Člen
Avatar
Michaal.K:9. května 11:26

Ahoj,
mám problém s předáváním dat mezi třídou a formulářem. V hlavním formu (MainForm) mám richTextBox ve kterém chci zobrazovat data přijatá po sériové lince. Pak mám třídu Komunikace.cs, která obsahuje definici sériového portu a je zde i událost DataReceived (takže pokud něco přijmu po seriové lince událost je vyvolána). To vše funguje, ale teď potřebuji string, který uložím ve třídě Komunikace.cs zobrazit v MainFormu v richTextBoxu a nevím jak na to.
Níže je třída Komunikace.cs:

class Komunikace
    {
        Parametry parametrySerialTrans = Parametry.Instance;                        // Databaze parametru pro seriovou linku a ulozeni dat
        SerialPort seriovyPort = new SerialPort();                                  // Novy seriovy port
        private bool connected;                                                     // Info o otevreni nebo zavreni serioveho portu

        // Konstruktor tridy Communication
        public Komunikace()
        {
            seriovyPort.PortName = parametrySerialTrans.ComPort;        // Nastavim nazev portu serioveho portu z tridy Parametry
            seriovyPort.BaudRate = parametrySerialTrans.Rychlost;  // Nastavim rychlost serioveho portu z tridy Parametry
            seriovyPort.Parity = parametrySerialTrans.Parita;             // Nastavim paritu serioveho portu z tridy Parametry
            seriovyPort.StopBits = parametrySerialTrans.StopBit;      // Nastavim stop bit serioveho portu z tridy Parametry

            seriovyPort.Handshake = Handshake.None;
            seriovyPort.ReadTimeout = 500;
            seriovyPort.WriteTimeout = 500;
            seriovyPort.DtrEnable = true;
            seriovyPort.RtsEnable = true;

            seriovyPort.DataReceived += SeriovyPort_DataReceived;
        }

        /// <summary>
        /// Zjistim zda je otevren nebo zavren seriovy port
        /// </summary>
        public bool ConnectedPorts
        {
            get { return this.connected; }
        }

        /// <summary>
        /// Otevre seriovou linku
        /// </summary>
        public string Pripojit()
        {
            try
            {
                if (!seriovyPort.IsOpen)
                {
                    seriovyPort.Open();                                             // Otevru dany seriovy port
                    this.connected = true;                                          // Nastavim na true pokud je nejaky seriovy port otevren
                    return "Port " + seriovyPort.PortName + " je otevřen";
                }
                else
                {
                    return "Port " + seriovyPort.PortName + " je otevřen";
                }
            }
            catch (Exception ex)
            {
                return "Port nelze otevřít: " + ex.Message;
            }
        }

        /// <summary>
        /// Zavre seriovou linku
        /// </summary>
        public string Odpojit()
        {
            if (seriovyPort.IsOpen)
            {
                try
                {
                    seriovyPort.Close();                                            // Zavru dany seriovy port
                    this.connected = false;                                         // Nastavim na false pokud je nejaky seriovy port uzavren
                    return "Port " + seriovyPort.PortName + " je zavřen";
                }
                catch (Exception ex)
                {
                    return ex.Message;
                }
            }
            else
            {
                return "Port " + seriovyPort.PortName + " je zavřen";
            }

        }

        /// <summary>
        /// Metoda, ktera je vyvolana pokud dojde k zapisu do prijimaciho stringu
        /// </summary>
        private void SeriovyPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            //throw new NotImplementedException();
            string data = seriovyPort.ReadExisting();
            //string data = seriovyPort.ReadLine();
        }
    }

Díky za rady...

Editováno 9. května 11:27
 
Odpovědět 9. května 11:26
Avatar
Jan Vargovský
Redaktor
Avatar
Odpovídá na Michaal.K
Jan Vargovský:9. května 18:55

Klasickým observer vzorem, třeba...

pseudokód:

class Komunikace
{
 public event Action<string> OnDataReceived;
 void SeriovyPort_DataReceived(...){
  string data = seriovyPort.ReadExisting();
  OnDataReceived?.Invoke(data);
}
}

class MainForm{
  ctor()
  {
      var komunikace = new Komunikace();
      komunikace.OnDataReceived +=str => richTextBoxu.Text += str;
  }
}
 
Nahoru Odpovědět 9. května 18:55
Avatar
Radek Chalupa:9. května 20:15

Osobně bych to řešil tak že bych do třídy komunikace přidat proměnnou typu reference na RichEdit a do kontruktoru komunikace jako parametr typ RichEdit a z MainFormu pak:
new komunikace(this->prislusny_riche­dit);
Třída komunikace si to v konstruktoru uloží do své proměnné a v té události zavolá this->rich_edit->Text = ...
Myslím že z hlediska rychlosti vykonání té akce to bude asi nejrychlejší řešení.

Radek Chalupa

  • konzultace a školení programování, (C/C++, C#, WinAPI, .NET, COM, ATL, MFC...)
  • vývoj software na zakázku

https://www.radekchalupa.cz

 
Nahoru Odpovědět  -3 9. května 20:15
Avatar
Michaal.K
Člen
Avatar
Odpovídá na Radek Chalupa
Michaal.K:10. května 10:15

Ahoj,
díky za odpověď. Mohl bys to trochu lépe rozvést a uvést nějaký příklad kódu. Děkuji

 
Nahoru Odpovědět 10. května 10:15
Avatar
Michaal.K
Člen
Avatar
Odpovídá na Jan Vargovský
Michaal.K:10. května 10:40

Ahoj, díky za odpověď. Tuto možnost vůbec neznám. Našel jsem článek na observer i zde na itnetwork, ale moc moudrej ztoho nejsem.
Zkoušel jsem tvůj kód doplnit a upravit, ale pořád to nefunguje.
Nevím jestli jsem dobře popsal svůj problém. Pokud mi po sériové lince přijdou nějaká data uloží se ve třídě Komunikace.cs do proměnné data a já bych potřeboval jakmile přidnou nějaké data tak aby se zobrazila v MainFormu v richTextBoxu.
Díky za pomoc

 
Nahoru Odpovědět 10. května 10:40
Avatar
Odpovídá na Michaal.K
Radek Chalupa:10. května 11:27

Jednoduchá ukázka - viz. podstatný úsek kódu:

class akce
{
    private RichTextBox _rtb = null;
    public akce(RichTextBox rtb)
    {
        this._rtb = rtb;
        neco_udelej();
    }

    private void neco_udelej()
    {
        if (null == this._rtb)
            throw new Exception("není nastaven textbox");
        this._rtb.Text = Environment.TickCount.ToString();
    }
}

public partial class Form1 : Form
{
    private akce _a = null;

    public Form1()
    {
        // Pokud bude akce ve vlastním threadu
        CheckForIllegalCrossThreadCalls = false;

        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        if (null != _a)
            return;
        _a = new akce(this.rich_text_1);
    }
}

Radek Chalupa

  • konzultace a školení programování, (C/C++, C#, WinAPI, .NET, COM, ATL, MFC...)
  • vývoj software na zakázku

https://www.radekchalupa.cz

 
Nahoru Odpovědět 10. května 11:27
Avatar
Marian Benčat
Redaktor
Avatar
Odpovídá na Radek Chalupa
Marian Benčat:10. května 12:14

Se vší úctou pane Chalupa (Céčkaře vždy uznávám), ale předávat do třídy určené k nějaké síťové komunikaci nějaké UI prvky je hloupost porušující snad úplně všechny pravidla...

Nějaká abstrakce? SoC? Enkapsulace?

Chápu, že jste přišel z C/C++ kde se třeba řeší výkon na každém kousku kódu, takže i indirect call je prostě znát, tak v HLL jazyku snad nebudemezahazovat všechny poučky pro správně napsaný software, aby jsme něco ušetřili?

Takovéto premature optimalizace, které způsobí 100 problémů jinde, mi nikdy nešly na rozum..

Nahoru Odpovědět 10. května 12:14
Totalitní admini..
Avatar
Michaal.K
Člen
Avatar
Odpovídá na Radek Chalupa
Michaal.K:10. května 12:46

Tak ve třídě komunikace v metodě SeriovyPort_Da­taReceived program vyhodí chybovou hlášku na řádku:

this.zapisLogu.AppendText(data);
Neošetřená výjimka typu System.InvalidOperationException vznikla v System.Windows.Forms.dll.
Další informace: Operace mezi vlákny není platná: Přístup k ovládacímu prvku richTextBoxLog proběhl z jiného vlákna než z vlákna, v rámci kterého byl vytvořen.
 
Nahoru Odpovědět 10. května 12:46
Avatar
Marian Benčat
Redaktor
Avatar
Odpovídá na Michaal.K
Marian Benčat:10. května 12:55

Ano, protože pan Chalupa zapomněl, že je to Single Thread Apartment, kde má přístup k UI prvkům pouze thread, který je vytvořil = hlavní UI thread. Tedy tyto UI "úpravy" musíš nějakým způsobem delegovat do hlavního vlákna.

A proto, by nějaká třída pro síťovou komunikaci (nebo cokoliv jiného na jiné vrstvě než je UI) v žádném případě neměla mít referenci na UI prvky, protože neví, jak ty UI prvky fungují a že běží v nějakém WinForms..

EDIT:
Nezapomněl, hackuje to:
CheckForIllegal­CrossThreadCa­lls = false;

Editováno 10. května 12:57
Nahoru Odpovědět 10. května 12:55
Totalitní admini..
Avatar
Marian Benčat
Redaktor
Avatar
Marian Benčat:10. května 12:58

Nikdy nedělej:
CheckForIllegal­CrossThreadCa­lls = false;

Je to hackování, které možná někdy bude fungovat, ale většinou ne.. K UI prvkům "odjinud" z jiných threadů musíš delegovat do hlavního threadu, třeba přes:
https://msdn.microsoft.com/…yzhdc6b.aspx

Akceptované řešení
+20 Zkušeností
+1 bodů
Řešení problému
Nahoru Odpovědět 10. května 12:58
Totalitní admini..
Avatar
Odpovídá na Marian Benčat
Radek Chalupa:10. května 14:16

Pokud programátor ví co dělá a vidí trochu pod povrch, je schopen rozhodnout kdy to bude fungovat. A když potřebuju udělat jenom jednoduchou aplikaci s jedním formem a jednou třídou na komunikaci, nemyslím že je třeba brát kanón na vrabce...
Samozřejmě pokud by ta zmíněná třída měla být vytvořena pro nějakou univerzální knihovnu, je to jiná situace.
Ostatně .NET je postaven nad Windows API a ve Windows API lze bez jakýchkoliv problému přistupovat k oknům (zde tedy UI prvkům) z jiného vlákna.

 
Nahoru Odpovědět 10. května 14:16
Avatar
Michaal.K
Člen
Avatar
Odpovídá na Marian Benčat
Michaal.K:10. května 14:21

Tak jsem to nakonec vyřešil nakonec takto:
Třída Komunikace.cs

class Komunikace
{
    SerialPort seriovyPort = new SerialPort();                                  // Novy seriovy port

    public delegate void DataReceivedEventHandler(string data);                 // Deklarace delagata, ktery bude slouzit jako predpis metody predstavujici reakci na udalost (handler)
    public event DataReceivedEventHandler OnSerialDataReceived;                 // Deklarace udalosti

    // Konstruktor tridy Komunikace
    public Komunikace()
    {
        seriovyPort.PortName = parametrySerialTrans.ComPort;                    // Nastavim nazev portu serioveho portu z tridy Parametry
        seriovyPort.BaudRate = parametrySerialTrans.Rychlost;                   // Nastavim rychlost serioveho portu z tridy Parametry
        seriovyPort.Parity = parametrySerialTrans.Parita;                       // Nastavim paritu serioveho portu z tridy Parametry
        seriovyPort.StopBits = parametrySerialTrans.StopBit;                    // Nastavim stop bit serioveho portu z tridy Parametry

        seriovyPort.Handshake = Handshake.None;
        seriovyPort.ReadTimeout = 500;
        seriovyPort.WriteTimeout = 500;
        seriovyPort.DtrEnable = true;
        seriovyPort.RtsEnable = true;

        seriovyPort.DataReceived += new SerialDataReceivedEventHandler(SeriovyPort_DataReceived);   // Prida udalost, ktera je vyvolana pokud dojde k zapisu do prijimaciho stringu
    }


    /// <summary>
    /// Metoda, ktera je vyvolana pokud dojde k zapisu do prijimaciho stringu
    /// </summary>
    private void SeriovyPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
    {
        Thread.Sleep(300);                                      // Cekam nez se objevi vsechny data v prijimacim stringu

        string data = seriovyPort.ReadExisting();
        //string data = seriovyPort.ReadLine();

        // Zkontroluju jestli je event nekde z venci teto tridy pouzit, kdyby nebyl nebudu ho volat, protoze je null = exception
        if (OnSerialDataReceived != null)
        {
            OnSerialDataReceived(data);
        }
    }
}

MainForm:

public partial class MainForm : Form
{
    private Komunikace komSeriovyPort = new Komunikace();                       // Komunikace se cteckou carovych kodu

    public MainForm()
    {
        InitializeComponent();

        komSeriovyPort.OnSerialDataReceived += new Komunikace.DataReceivedEventHandler(komSeriovyPort_OnSerialDataReceived);        // Prida udalost, ktera je vyvolana pokud dojde k zapisu do prijimaciho stringu
    }

    /// <summary>
    /// Metoda se vyvola pokud dojde k zapisu do prijimaciho stringu
    /// </summary>
    public void komSeriovyPort_OnSerialDataReceived(string data)
    {
        this.Invoke(new EventHandler(delegate
        {
            if (data[(data.Length) - 1] == '\r')                                // Testuji zda nakonci prijateho retezce je znak \r
            {
                data = data.Remove((data.Length) - 1);                          // smazu znak \r ktery zpusoboval dalsi odradkovani.
            }

            richTextBoxLog.AppendText("[" + DateTime.Now.ToShortTimeString() + "] " + data);     // Dany text zapisu do richTextBoxLog
            richTextBoxLog.ScrollToCaret();                                     // Posunu kurzor na konec, tim dosahnu rolovani textu
            richTextBoxLog.Refresh();                                           // Provedu refresh richtextBoxu
        }));
    }
}

Prosím o kontrolu jestli to tak může být. Zatím to funguje jak má.
Jestli to není nějak krkolomně napsané.
Díky moc

 
Nahoru Odpovědět  +1 10. května 14:21
Avatar
Marian Benčat
Redaktor
Avatar
Odpovídá na Radek Chalupa
Marian Benčat:10. května 14:35

Myslím si, že jestli je něco kanón na vrabce, tak je to především vaše optimalizace v tomto případě a hackování threading modelu právě "runtime" ve kterém pracujete...

To, že se impliciítně nesmí šahat do hlavního UI threadu z jiných vláken má svoje hodně dobré důvody, které jistě znáte, tak nevím, proč je obcházet, obzvláště, pokud je řešení poměrfně dost jednoduché a comprehensible.

Díky bohu, nový a rozumný .NET už nad Windows API postavený není.

Editováno 10. května 14:38
Nahoru Odpovědět 10. května 14:35
Totalitní admini..
Avatar
Marian Benčat
Redaktor
Avatar
Odpovídá na Michaal.K
Marian Benčat:10. května 14:38

Základní pointa je správně, tedy Invoke() na controlu (způsob "delegování" do hlavního threadu)

Nahoru Odpovědět 10. května 14:38
Totalitní admini..
Avatar
Odpovídá na Marian Benčat
Radek Chalupa:10. května 15:23

Upřímně řečeno právě že nevím jaké jsou ty zmíněné hodně dobré důvody nepřistupovat ke GUI přes vlákna. Uvítám nějaký příklad.

A nad čím je postavený ten "nový a rozumný net"? To že je tam nějaká multiplatformní mezivrstva přece neznamená že ta implementace na "spodní" úrovni nevolá příslušné sužby/API toho kterého systému?

 
Nahoru Odpovědět 10. května 15:23
Avatar
Marian Benčat
Redaktor
Avatar
Odpovídá na Radek Chalupa
Marian Benčat:10. května 16:28

Tak především to, že pokud je něco striktně určeno pro použití v STA (třeba jako celé WinForms / WPF), tak nejsou v základu threadsafe a jakmile něco není threadsafe tak přístup k tomu z více threadu (třeba v tomto případě main UI thread a ostatní) může způsobit poměrně vážné problémy..(to už ale jistě víte).

A proč nejsou ty controly threadsafe? Protože je mnohem lepší je nedělat implicitně threadsafe a locknout jejich použití do single thread prostředí, protože je to:

  • výkonné (žádný locking na úrovni každé komponety - navíc CAS instrukce by nebyly zde ani použitelné, ani vhodné)
  • bezpečné (žádný multithread race condition)
  • jednoduché na implementaci a použití

To co jsem napsal o :NET, tím jsem myslel, že v něm není přímo zadrátování winapi a "iisko".

Nahoru Odpovědět 10. května 16:28
Totalitní admini..
Avatar
Marian Benčat
Redaktor
Avatar
Marian Benčat:10. května 16:30

Mimochodem, mám pocit (ale ruku do ohně za to nedám), že:
CheckForIllegal­CrossThreadCa­lls = false

stále způsobuje vyhazování exception, jen se nějakým způsobem handlí. Takže co jste získal tím posláním UI prvků do třídy komunikace, jste 10x ztratil exceptionou a chain of responsibility

Nahoru Odpovědět 10. května 16:30
Totalitní admini..
Avatar
Jan Vargovský
Redaktor
Avatar
Odpovídá na Michaal.K
Jan Vargovský:11. května 6:44

Jo, jestli ti nevadí, že je to psáno stylem jak v roku 2005, tak je to ok.

Když teda zavřu oči u toho Thread.Sleep(...) :D

Editováno 11. května 6:44
 
Nahoru Odpovědět 11. května 6:44
Avatar
Jan Vargovský
Redaktor
Avatar
Jan Vargovský:11. května 6:56

Pánové diskutující trošku OT.

Chápu přístup obou z Vás a v podstatě bych souhlasil s oběma, kdyby tam nebyl ten hack na CheckForIllegal­CrossThreadCa­lls.

Radek Chalupa Ten předpoklad "pokud programátor ví" bych v tomhle případě zamítnul. Jestli píšu utilitu sám pro sebe, tak to tam klidně můžete takhle naprasit, ale nedovolil bych si to napsat nikde do kódu, který by viděl někdo další. Udělat to "správně" zabere asi tak 15 sekund. Nehledě na to, že jestli tam něco bude editovat, tak se bude méně bít do hlavy, že hledá UI funkcionalitu po všech souborech.

@Marian Benčat (bohužel přestalo fungovat označování lidí :D ) na můj vkus bereš až moc dogmaticky všechny design patterny a best practices (ať už je to modularita, solid nebo cokoliv jiného).

ad nový .NET) a co .net core 3? Já jsem zvědavý jakým způsobem to chcou portnout na ostatní platformy (první jsem si myslel, že to prostě jen bude WF/WPF atd. na .NET Coru pro windows only, ale když to má být i v .net standardu, tak už to cross platform "musí" být)

 
Nahoru Odpovědět 11. května 6:56
Avatar
Michaal.K
Člen
Avatar
Odpovídá na Jan Vargovský
Michaal.K:11. května 7:41

Díky za info. No tak vadí, že je to napsaný jak z roku 2005 :-) Mohl bys mi kód poupravit, aby odpovídal novým trendům nebo případně vysvětlit co napsat jinak?
Rád si doplním znalosti...
Díky moc

 
Nahoru Odpovědět 11. května 7:41
Avatar
Marian Benčat
Redaktor
Avatar
Odpovídá na Jan Vargovský
Marian Benčat:11. května 9:14

Já jsem úplně poslední člověk co se řídí dogmaty, ale předávat si do nějaké komunikující třídy UI control, za to by můj podřízený letěl, to je pravda

Nahoru Odpovědět  ±0 11. května 9:14
Totalitní admini..
Avatar
Jan Vargovský
Redaktor
Avatar
Odpovídá na Marian Benčat
Jan Vargovský:11. května 15:18

Líbí se mi tvůj přístup, nehledáte vývojáře? :D

 
Nahoru Odpovědět  +1 11. května 15:18
Avatar
Marian Benčat
Redaktor
Avatar
Odpovídá na Jan Vargovský
Marian Benčat:11. května 15:25

:D hledáme :D v jednom kuse mizí.. :D

Editováno 11. května 15:25
Nahoru Odpovědět 11. května 15:25
Totalitní admini..
Avatar
Jan Vargovský
Redaktor
Avatar
Odpovídá na Michaal.K
Jan Vargovský:11. května 15:28

definovat vlastní delegát už je dle mě zbytečné, vystačíš si s Action<...>, Func<...>.

public delegate void DataReceivedEventHandler(string data);                 // Deklarace delagata, ktery bude slouzit jako predpis metody predstavujici reakci na udalost (handler)
    public event DataReceivedEventHandler OnSerialDataReceived;                 // Deklarace udalosti

na

public event Action<string> OnSerialDataReceived;

Invoke už nemusíš dělat přes delegáte, ale stačí ti anonymní metoda

this.Invoke(new EventHandler(delegate
        {
            if (data[(data.Length) - 1] == '\r')                                // Testuji zda nakonci prijateho retezce je znak \r
            {
                data = data.Remove((data.Length) - 1);                          // smazu znak \r ktery zpusoboval dalsi odradkovani.
            }

            richTextBoxLog.AppendText("[" + DateTime.Now.ToShortTimeString() + "] " + data);     // Dany text zapisu do richTextBoxLog
            richTextBoxLog.ScrollToCaret();                                     // Posunu kurzor na konec, tim dosahnu rolovani textu
            richTextBoxLog.Refresh();                                           // Provedu refresh richtextBoxu
        })

na

this.Invoke(() =>
        {
             ...
        })

pak

komSeriovyPort.OnSerialDataReceived += new Komunikace.DataReceivedEventHandler(komSeriovyPort_OnSerialDataReceived);

ne

komSeriovyPort.OnSerialDataReceived += komSeriovyPort_OnSerialDataReceived;

já bych nepsal tohle:

SerialPort seriovyPort = new SerialPort();
seriovyPort.PortName = parametrySerialTrans.ComPort;                    // Nastavim nazev portu serioveho portu z tridy Parametry
        seriovyPort.BaudRate = parametrySerialTrans.Rychlost;                   // Nastavim rychlost serioveho portu z tridy Parametry
        seriovyPort.Parity = parametrySerialTrans.Parita;                       // Nastavim paritu serioveho portu z tridy Parametry
        seriovyPort.StopBits = parametrySerialTrans.StopBit;                    // Nastavim stop bit serioveho portu z tridy Parametry

        seriovyPort.Handshake = Handshake.None;
        seriovyPort.ReadTimeout = 500;
        seriovyPort.WriteTimeout = 500;
        seriovyPort.DtrEnable = true;
        seriovyPort.RtsEnable = true;

ale radši tohle:

        seriovyPort = new SerialPort {
        Handshake = Handshake.None,
        ReadTimeout = 500,
        WriteTimeout = 500,
        DtrEnable = true,
        RtsEnable = true,
}

protože pak máš hned výčet propert v intellisense, které ti chybí nainicializovat.

if (OnSerialDataReceived != null)
        {
            OnSerialDataReceived(data);
        }

na

OnSerialDataReceived?.Invoke(data);

Už jen třeba proto, že to tvoje není threadsafe a tohle je jako atomická operace (tohle tě nemusí trápit, ale do budoucna používej raději elvis operátor.

Editováno 11. května 15:30
 
Nahoru Odpovědět 11. května 15:28
Avatar
Jan Vargovský
Redaktor
Avatar
Odpovídá na Marian Benčat
Jan Vargovský:11. května 15:31

Jestli člověk jednou napíše něco nesmyslného a už není, tak se není čemu divit :D

 
Nahoru Odpovědět 11. května 15:31
Avatar
Odpovídá na Marian Benčat
Erik Šťastný:11. května 15:49

Pokud je to myšleno vážně, tak programátorovi je to asi fuk, i ti špatní si v dnešní době dokážou najít rychle práci.

Spíše vy jste ti co pořád dokola hledáte nového člověka, než aby jste si vychovali vlastního a odnaučili ho špatné věci. U nás se teda postupuje zcela opačným způsobem.

 
Nahoru Odpovědět 11. května 15:49
Avatar
Jan Vargovský
Redaktor
Avatar
Odpovídá na Erik Šťastný
Jan Vargovský:11. května 16:04

Zrovna věnuju kolegovi celý můj den a řeším s ním jeho úkol. Všude se převychovávají lidi, mě osobně baví lidi učit a předávat jim své zkušenosti. Rád bych ale zažil team, kdy každý ví co dělat a řešili byste spolu fakt jen takové špeky, jestli někoho nenapadne něco lepšího.

 
Nahoru Odpovědět 11. května 16:04
Avatar
Marian Benčat
Redaktor
Avatar
Odpovídá na Jan Vargovský
Marian Benčat:12. května 9:32

Pak musíš nabírat jen lidi stejné kvality jako jsi ty.

Nahoru Odpovědět 12. května 9:32
Totalitní admini..
Avatar
Luboš Satik Běhounek
Autoredaktor
Avatar
Odpovídá na Marian Benčat
Luboš Satik Běhounek:12. května 13:04

Pak musíš nabírat jen lidi stejné kvality jako jsi ty.

Nahoru Odpovědět  +1 12. května 13:04
https://www.facebook.com/peasantsandcastles/
Avatar
Marian Benčat
Redaktor
Avatar
Odpovídá na Luboš Satik Běhounek
Marian Benčat:12. května 15:28

..nebo lepší... :D

Nahoru Odpovědět 12. května 15:28
Totalitní admini..
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 31 zpráv z 31.