Καλώς ορίσατε στο dotNETZone.gr - Σύνδεση | Εγγραφή | Βοήθεια
σε

 

Αρχική σελίδα Ιστολόγια Συζητήσεις Εκθέσεις Φωτογραφιών Αρχειοθήκες

Pass an object sender and compare the properties...

Îåêßíçóå áðü ôï ìÝëïò Τάσκος Γιώργος. Τελευταία δημοσίευση από το μέλος Τάσκος Γιώργος στις 04-01-2009, 19:07. Υπάρχουν 12 απαντήσεις.
Ταξινόμηση Δημοσιεύσεων: Προηγούμενο Επόμενο
  •  04-01-2009, 02:59 47398

    Pass an object sender and compare the properties...

    Παιδιά θα χρειαζόμουν λίγο βοήθεια εδώ γιατί το έχασα κάπου.

    Έχω ένα backgroundWorker και 2 κουμπιά που πατώντας ανάλογα
    θέλω να κάνει κάτι διαφορετικό. Πατώντας το κάθε κουμπί εκτελείται η
    μεθοδός, backgroundWorker.RunWorkerAsync(sender); όπου υποτίθεται
    ότι το sender το περνάω από το button1_Click ή button2_Click ανάλογα.
    Αυτό ποy χρειάζομαι τώρα είναι να κάνω τον έλεγχο για το ποιό κουμπί
    πατήθηκε στο backgroundWorker_DoWork(object sender, DoWorkEventArgs e) event,
    και ανάλογα να καλέσω τις μεθόδους που θέλω. Έλα όμως που έχω χαθεί εκεί και
    παλεύω με τα cast και δεν μου βγαίνει. Το λάθος που μου πετάει συνεχώς είναι οτι
    δεν μπορεί να κάνει cast :

    Unable to cast object of type 'System.ComponentModel.BackgroundWorker' to type 'System.Windows.Forms.Button'.

    OK το καταλαβαίνω, αλλά πως θα πετύχω το αποτέλεσμα που θέλω?

    Ευχαριστώ.

  •  04-01-2009, 13:06 47402 σε απάντηση της 47398

    Απ: Pass an object sender and compare the properties...

    Το κουμπί που πατήθηκε και περνάς σαν παράμετρο στην RunWorkerAsync θα το βρεις στο property Argument της παραμέτρου e (τύπου DoWorkEventArgs) που περνιέται στον event handler σου και όχι στην παράμετρο sender, η οποία είναι το αντικείμενο τύπου BackgroundWorker για το οποίο έχει κληθεί ο event handler.
    Νατάσα Μανουσοπούλου
  •  04-01-2009, 14:24 47404 σε απάντηση της 47398

    Απ: Pass an object sender and compare the properties...

    Μήπως θα έπρεπε να χρησιμοποιήσεις δύο BackgroundWorkers; Τι θα γίνει όταν ενώ τρέχει το ένα process κάνεις click στο άλλο button; Αυτό που θέλεις να κάνεις γίνεται, αλλά εγώ θα έβαζα μια flag variable ανάλογα με το ποιο button έχει πατηθεί και στο DoWork event με ένα switch - case θα καλούσα την αντίστοιχη μέθοδο. Τέλος, στο RunWorkerCompleted event θα έκανα reset στη flag μεταβλητή.


    Ακόμα κι ένας άνθρωπος μπορεί ν' αλλάξει τον κόσμο. Μη θέλεις να κυβερνήσεις. Απλά δείξε το μονοπάτι κι ο κόσμος θ' ακολουθήσει!!
  •  04-01-2009, 14:27 47405 σε απάντηση της 47398

    Απ: Pass an object sender and compare the properties...

    Έχεις παρεξηγήσει τον τρόπο λειτουργίας των events γενικότερα. Ένας event handler δέχεται ως πρώτη παράμετρο (sender) το αντικείμενο το οποίο σήκωσε το event και ως δεύτερη παράμετρο τα δεδομένα που αφορούν το event (e).
    Το DoWork event σηκώνεται από το backgroundworker οπότε η παράμετρος sender περιέχει το BackgroundWorker και όχι το αντικείμενο το οποίο πέρασες ως argument στην RunWorkerAsync. Αυτό φτάνει στην DoWorker μέσω των δεδομένω του event, δηλαδή την παράμετρο e. Η παράμετρος αυτή είναι τύπου DoWorkerEventArgs και περιέχει το property Argument με το αντικείμενο που έδωσες στην RunWorkerAsync.

    Όλα αυτά μπορείς να τα βρεις στο documentation και τα παραδείγματα του BackgroundWorker.DoEvent, της BackgroundWorker.RunWorkerAsync και της DoWorkerEventArgs


    Παναγιώτης Καναβός, Freelancer
    Twitter: http://www.twitter.com/pkanavos
  •  04-01-2009, 14:34 47407 σε απάντηση της 47405

    Απ: Pass an object sender and compare the properties...

    Marko, κάνω disable τα buttons όταν ξεκινήσει η διαδικασία. Με δύο workers θα έβγαινε ποιο εύκολα,
    αλλά λέω να παιδευτώ λιγάκι. Γρήγορη λύση είναι η μεταβλητή που λές...έπρεπε να το είχα σκεφτεί :)

    Παναγιώτη, η αλήθεια είναι όχι ότι τα έχω παρεξηγήσει αλλά δεν τα έχω καταλάβει ακόμα καλά πως δουλεύουν.
    Θα δώ τα documentations άμεσα. Ελπίζω να έχει και κάνα παράδειγμα. :)


    Ευχαριστώ παιδιά, θα δώσω απάντηση για να το κλείσω κιόλας, απλά θέλω να προσπαθήσω λίγο
    με το Argument.

  •  04-01-2009, 15:11 47408 σε απάντηση της 47407

    Απ: Pass an object sender and compare the properties...

    Δε νομίζω ότι υπάρχει κάτι το φοβερό με το Arguments. Ορίστε ένα παράδειγμα βάσει αυτού που περιέγραψες ότι θέλεις να κάνεις. Η φόρμα έχει δύο buttons και δύο textboxes (ένα για input και ένα για την εμφάνιση του αποτελέσματος):

    public partial class Form1 : Form
    {
       private short button = 0; // Flag variable. 0 = Δεν έχει πατηθεί κανένα button
       //
       public Form1()
       {
          InitializeComponent();
          //
          btnSum.Enabled = true; // Αρχικά και τα δύο buttons είναι enabled
          btnSqrtSum.Enabled = true;
       }

       private double CalcSum(int bound) // Υπολόγισε το άθροισμα των ακεραίων
       {
          double sum = 0;
          for (int i = 1; i <= bound; i++)
          {
             sum = sum + i;
          }
          return sum;
       }

       private double CalcSqrtSum(int bound) // Υπολόγισε το άθροισμα των τετραγωνικών ριζών των ακεραίων
       {
          double sqrtSum = 0;
          for (int i = 1; i <= bound; i++)
          {
             sqrtSum = sqrtSum + Math.Sqrt((double)i);
          }
          return sqrtSum;
       }

       private void btnSum_Click(object sender, EventArgs e)
       {
          btnSum.Enabled = false;
          btnSqrtSum.Enabled = false;
          textBox2.Text = ""; // Στο TextBox2 εμφανίζω το αποτέλεσμα της επεξεργασίας
          //
          int bound = int.Parse(this.textBox1.Text.Trim()); // Στο TextBox1 εισάγω τη μέγιστη θετική ακέραια τιμή
          button = 1;
          backgroundWorker1.RunWorkerAsync(bound); // Δηλώνω στο argument ποιος είναι ο μέγιστος θετικός ακέραιος για τον οποίο θέλω να γίνει ο υπολογισμός
       }

       private void btnSqrtSum_Click(object sender, EventArgs e)
       {
          btnSum.Enabled = false;
          btnSqrtSum.Enabled = false;
          textBox2.Text = "";
          //
          int bound = int.Parse(this.textBox1.Text.Trim());
          button = 2;
          backgroundWorker1.RunWorkerAsync(bound);
       }

       private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
       {
          int bound;
          switch (button)
          {
             case 1:
                bound = (int)e.Argument; // Παίρνω από το argument το όριο του μέγιστου θετικού ακεραίου
                e.Result = CalcSum(bound); // και το χρησιμοποιώ για να καλέσω τη μέθοδο υπολογισμού
                break;
             case 2:
                bound = (int)e.Argument;
                e.Result = CalcSqrtSum(bound);
                break;
          }
       }

       private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
       {
          textBox2.Text = e.Result.ToString(); // Στο TextBox2 εμφανίζω το αποτέλεσμα του υπολογισμού.
          button = 0; // Κάνε reset στη flag μεταβλητή.
          //
          btnSum.Enabled = true;
          btnSqrtSum.Enabled = true;
       }

    }


    Ακόμα κι ένας άνθρωπος μπορεί ν' αλλάξει τον κόσμο. Μη θέλεις να κυβερνήσεις. Απλά δείξε το μονοπάτι κι ο κόσμος θ' ακολουθήσει!!
  •  04-01-2009, 15:28 47409 σε απάντηση της 47408

    Απ: Pass an object sender and compare the properties...

    Μάρκο, η πρόταση σου θα ήταν σωστή αν δεν είχαμε να κάνουμε με threads. Η μεταβλητή που χρησιμοποιείς βρίσκεται σε διαφορετικό thread από το DoWork event. Μπορεί σε αυτό το απλό παράδειγμα να δουλέψει υπό προϋποθέσεις, ακόμα κι εδώ όμως μπορούν να δημιουργηθούν προβλήματα. Αν κατά λάθος η RunWorkerAsync κληθεί πριν αλλάξεις την τιμή του button σε κάποιο Click event θα έχεις πιθανό race condition. Το ίδιο θα συμβεί αν ξεχάσεις κάπου να κάνεις disable κάποιο κουμπί. Στη γενική περίπτωση, αν η DoWork διαβάζει την τιμή κάποιου πεδίου της φόρμας θα πρέπει να χρησιμοποιηθούν locks για να εξασφαλιστεί η σωστή εκτέλεση του κώδικα.

    Το πρόβλημα εξαφανίζεται αν καλέσεις την RunWorkerAsync με argument, καθώς το BackgroundWorker αναλαμβάνει να περάσει την τιμή στην DoWork με ασφαλή τρόπο. Αν θέλεις να περάσεις περισσότερα από ένα arguments, φτιάξε μία κλάση με τα πεδία που χρειάζεσαι και πέρνα τη ως argument στην RunWorkerAsync.


    Παναγιώτης Καναβός, Freelancer
    Twitter: http://www.twitter.com/pkanavos
  •  04-01-2009, 15:44 47410 σε απάντηση της 47409

    Απ: Pass an object sender and compare the properties...

    Έχεις δίκιο. Το καλύτερο θα ήταν δύο BackgroundWorkers. Προσπαθεί να βάλει δύο καρπούζια κάτω από την ίδια μασχάλη.  Αν υιοθετήσει τον ορθόδοξο τρόπο αποφεύγει και τη δημιουργία μιας extra κλάσης που θα περάσει σαν argument. Με αυτό το απλοποιημένο παράδειγμα το λάθος μπορεί να γίνει εύκολα αν δεν υπάρξει προσοχή στην υλοποίηση.


    Ακόμα κι ένας άνθρωπος μπορεί ν' αλλάξει τον κόσμο. Μη θέλεις να κυβερνήσεις. Απλά δείξε το μονοπάτι κι ο κόσμος θ' ακολουθήσει!!
  •  04-01-2009, 16:34 47411 σε απάντηση της 47410

    Απ: Pass an object sender and compare the properties...

    Δεν εννοούσα αυτό. Δεν χρειάζονται δύο BackgroundWorkers για να εκτελέσεις διαφορετικό κώδικα, καθώς δεν υπάρχει καμμία προσπάθεια να γίνουν δύο διαφορετικά πράγματα ταυτόχρονα. Ο BackgroundWorker έτσι κι αλλιώς δεν επιτρέπει να κληθεί η RunWorkerAsync όσο εκτελείται ακόμα κώδικας στην DoWork. Τί κώδικας θα τρέξει εκεί μέσα, είναι θέμα του προγραμματιστή. Άνετα μπορείς να επιλέξεις να τρέξεις διαφορετικό κώδικα με βάση το argument.
    Μάλιστα, θα μπορούσες να χρησιμοποιήσεις το Command pattern και να δημιουργήσεις μία κλάση για κάθε διαφορετικό τύπο υπολογισμού με μία κοινή μέθοδο Execute. Αυτή την κλάση μπορείς μετά να την περάσεις στην RunWorkerAsync και η DoWork απλά να εκτελέσει την Execute της κλάσης που δέχεται. Έτσι θα μπορούσες να κρατήσεις τον κώδικα της DoWork τον ίδιο αλλά να εκτελέσεις όσους διαφορετικούς υπολογισμούς θέλεις. Προφανώς αυτή η λύση είναι υπερβολική αν απλά έχεις να επιλέξεις μεταξύ δύο απλών εναλλακτικών αλλά είναι πολύ χρήσιμη αν έχεις να εκτελέσεις πολλών διαφορετικών ειδών υπολογισμούς και δεν θέλεις να γεμίσει η εφαρμογή σου ασύγχρονες κλήσεις.

    Επίσης, ο "ορθόδοξος" τρόπος είναι να περνάς τα δεδομένα μέσω του argument και φυσικά η δημιουργία της extra κλάσης. Αν κάνεις κάτι διαφορετικό τότε αρχίζουν οι δυσκολίες.

    Όσον αφορά το Command Pattern, μπορείς να δεις τον παρακάτω κώδικα. Μέσα στη φόρμα δημιουργώ δύο κλάσεις οι οποίες υλοποιούν το IWork interface με μία και μοναδική μέθοδο double Execute(). Η μία αθροίζει μία λίστα από αριθμούς και η άλλη αθροίζει τις ρίζες τους. Το αποτέλεσμα είναι ότι η DoWork απλοποιείται πάρα πολύ αλλά μπορώ να φτιάξω όσο περίπλοκους υπολογισμούς θέλω. Από τη στιγμή που όλα τα απαραίτητα δεδομένα για τον υπολογισμό τα περνάω στην XXXWork ως πεδία, δεν με απασχολούν και θέματα concurrency.

        public partial class WorkerForm : Form
        {
            interface IWork
            {
                double Execute();
            }

            private class SumWork : IWork
            {
                public List<double> numbers;
                public double Execute()
                {               
                    double sum=0;
                    for (int i = 0; i < numbers.Count;i++ )
                        sum += numbersIdea;
                    return sum;
                }
            }

            private class SqrtWork : IWork
            {
                public List<double> numbers;
                public double Execute()
                {
                    double sum = 0;
                    for (int i = 0; i < numbers.Count; i++)
                        sum += Math.Sqrt(numbersIdea);
                    return sum;
                }
            }


            public WorkerForm()
            {
                InitializeComponent();
            }

            /// <summary>
            /// Φτιάχνω ένα SumWork και το γεμίζω 100 τυχαίους αριθμούς
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void btnSum_Click(object sender, EventArgs e)
            {
                List<double> numbers = CreateNumbers(100);
                SumWork work = new SumWork();
                work.numbers = numbers;
                backgroundWorker1.RunWorkerAsync(work);

            }

            /// <summary>
            /// Φτιάχνω ένα SqrtWork και το γεμίζω 100 τυχαίους αριθμούς
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void btnSqrt_Click(object sender, EventArgs e)
            {
                List<double> numbers = CreateNumbers(100);
                SqrtWork work = new SqrtWork();
                work.numbers = numbers;
                backgroundWorker1.RunWorkerAsync(work);
            }

            /// <summary>
            /// Δημιουργεί μία λίστα από τυχαίους αριθμούς
            /// </summary>
            /// <param name="count"></param>
            /// <returns></returns>
            private static List<double> CreateNumbers(int count)
            {
                List<double> numbers = new List<double>();
                Random r = new Random();
                for (int i = 0; i < count; i++)
                    numbers.Add(r.NextDouble());
                return numbers;
            }
           
            private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
            {
                IWork work = (IWork)e.Argument;
                //Το μόνο που χρειάζεται είναι να καλέσω την Execute
                e.Result = work.Execute();
            }

            private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
            {
                lblResult.Text = e.Result.ToString();
            }
        }

     


    Παναγιώτης Καναβός, Freelancer
    Twitter: http://www.twitter.com/pkanavos
  •  04-01-2009, 16:56 47412 σε απάντηση της 47411

    Απ: Pass an object sender and compare the properties...

    Παναγιώτης Καναβός:

    Επίσης, ο "ορθόδοξος" τρόπος είναι να περνάς τα δεδομένα μέσω του argument και φυσικά η δημιουργία της extra κλάσης. Αν κάνεις κάτι διαφορετικό τότε αρχίζουν οι δυσκολίες.

    Κατάλαβα τι εννοούσες. Απλά από τα γραφόμενα του μέλους "gtas" φάνηκε ότι προσπαθούσε να περάσει σαν argument ένα button και τίποτε άλλο. Συνεπώς, μάλλον δε χρειαζόταν μια extra κλάση. Δύο BackgroundWorkers θα απλοποιούσαν για εκείνον τα πράγματα. Βέβαια, μόνο ο ίδιος γνωρίζει τι θέλει να κάνει. Ο κώδικάς σου είναι αφοπλιστικός. Η υλοποίηση είναι πολύ πιο concrete και όμορφη. Επίσης κοίταζα και στο MSDN την τεκμηρίωση της BackroundWorker Class. Στο συγκεκριμένο παράδειγμα μου έκανε εντύπωση που έπαιρνε το instance του BackgroundWorker και το περνούσε σαν παράμετρο στη μέθοδο υπολογισμού ComputeFibonacci(). Ενδιαφέρον...

    Σημ.: Κάτι πρέπει να γίνει με το Idea = [ i ]. Αν δεν αφεθούν κενά ανάμεσα στην παράμετρο και τις αγκύλες εμφανίζονται γλόμποι!!


    Ακόμα κι ένας άνθρωπος μπορεί ν' αλλάξει τον κόσμο. Μη θέλεις να κυβερνήσεις. Απλά δείξε το μονοπάτι κι ο κόσμος θ' ακολουθήσει!!
  •  04-01-2009, 17:57 47413 σε απάντηση της 47412

    Απ: Pass an object sender and compare the properties...

    Εγώ το έλυσα παιδιά το προβλημά μου με τον ποιο απλό τρόπο της κοινής μεταβλητής,
    γιατί δεν χρειαζόταν κάποια ποιο ιδιαίτερη λύση για να καταφέρω αυτό που ήθελα στην
    συγκεκριμένη περίπτωση, αλλά παρακολουθώ όλες τις παρατηρήσεις σας και βλέπω και στα documentation
    ότι το thread θέλει ασχολία....

    Τουλάχιστον θα έχω να κάνω κάτι... :P

    Σας ευχαριστώ για το ενδιαφέρον.

  •  04-01-2009, 18:29 47414 σε απάντηση της 47413

    Απ: Pass an object sender and compare the properties...

    Κάτσε για να καταλάβω. Εκτός από αυτή τη μία μεταβλητή, υπάρχουν άλλες που πρέπει να περάσουν σαν argument; Αν ναι τότε πρέπει να ξαναδιαβάσεις ό,τι έχει γράψει ο Παναγιώτης και να δημιουργήσεις μια extra κλάση στην οποία θα ορίζονται τα πεδία των παραμέτρων και θα περνάς το αντίστοιχο object σαν argument. Αλλιώς, ό,τι έγραψε ο άνθρωπος πάει στράφι...
    Ακόμα κι ένας άνθρωπος μπορεί ν' αλλάξει τον κόσμο. Μη θέλεις να κυβερνήσεις. Απλά δείξε το μονοπάτι κι ο κόσμος θ' ακολουθήσει!!
  •  04-01-2009, 19:07 47415 σε απάντηση της 47414

    Απ: Pass an object sender and compare the properties...

    Τίποτα δεν πάει στράφι.

    Πολύ καλά υπέδειξα τους σωστούς και ορθόδοξους τρόπους ο Παναγιώτης.

    Εγώ αυτό που ήθελα είναι απλά να κάνω έναν έλεγχο και διάλεξα να το κάνω
    σε μια μεταβλητή. Σε άλλες ποιο πολύπλοκες περιπτώσεις θα πρέπει να κάνεις
    την υλοποίηση που λέει ποιο πάνω ο άνθρωπος.

    Πρέπει να αλλαχτεί και το θέμα, γιατί δεν έπρεπε να περάσω τον sender.....


Προβολή Τροφοδοσίας RSS με μορφή XML
Με χρήση του Community Server (Commercial Edition), από την Telligent Systems