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

 

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

Μία Wrapper Class που μαζεύει όλα τα Controls μίας φόρμας

Îåêßíçóå áðü ôï ìÝëïò SValsamis. Τελευταία δημοσίευση από το μέλος SValsamis στις 14-02-2006, 13:15. Υπάρχουν 11 απαντήσεις.
Ταξινόμηση Δημοσιεύσεων: Προηγούμενο Επόμενο
  •  10-02-2006, 22:13 9525

    Μία Wrapper Class που μαζεύει όλα τα Controls μίας φόρμας

    Σε πολλές περιπτώσεις θέλουμε να πάρουμε τα Controls μίας φόρμας ανεξάρτητα αν αυτά βρίσκονται κατ' ευθείαν επάνω σε αυτή ή σε κάποιον άλλο Container.

    Πιο κάτω υπάρχει ο κώδικας ενός Class με ένα property Controls που έχει μέσα όλα τα Controls ενός Container

    Imports System.Windows.forms
    Imports System.componentmodel
    'Αυτή είναι μία Wrapper Class που δημιουργεί ένα
    'Collection από τα Controls ενός οποιουδήποτε Container
    Public Class MyControlsCollection
          
    Private Shared _Controls As Collection
          
    'Κατά τη δημιουργία της της περνάμε τη φόρμα της οποίας θέλουμε 
         
    'την Controls Collection

          Public Sub New(ByVal Container As Object)
                   _Controls =
    New Collection
                   
    'Δημιουργούμε έναν ControlCollector για τον Container Frm
                   
    Dim ControlCollector As New ControlCollector(Container)
          
    End Sub

          'Αυτό το property επιστρέφει όλα τα Controls που υπάρχουν στη
          'φόρμα σαν ένα ενιαίο Collection
          Public ReadOnly Property Controls() As Collection
                   
    Get
                         Return _Controls
                   
    End Get

          End Property

          'Αυτό το SubClass μαζεύει όλα τα Controls από έναν Container
          
    'και όλους τους containers που περιέχει αυτός ο Container
          
    Private Class ControlCollector
                      
    Private _Container As Object


                      
    Public Sub New(ByVal Container As Object)
                            
    'Αν ο Container περιέχει controls
                            
    If Container.haschildren Then
                               
    'Για κάθε control του Container
                               
    For Each Ctrl As Control In Container.controls
                                     
    'To _Controls Collection είναι Shared
                                     
    'και του προσθέτουμε το Control που βρήκαμε
                                     
    Dim ct As Object = Nothing
                                     
    Try
                                           
    ct = _Controls.Item(Ctrl.Name)
                                     
    Catch ex As Exception
                                     
    End Try

                                     If ct Is Nothing Then
                                        
    _Controls.Add(Ctrl, Ctrl.Name)
                                        
    'Αν το Control είναι Container (βλέπε Function)
                                        
    If IsContainer(Ctrl) AndAlso Ctrl.HasChildren Then
                                           
    'Δημιουργούμε έναν ControlCollector για τον Container Ctrl
                                           'με αυτό τον τρόπο φτάνει σε όσο βάθος θέλουμε
                                           
    Dim _Collector As New ControlCollector(Ctrl)
                                      
    End If
                                  
    End If
                              
    Next Ctrl
                          End If
                    
    End Sub

                   'Eπειδή στο .Net όλα τα Controls είναι Control Containers θα θέλαμε να ξεχωρίσουμε αυτά που είναι Containers 
                   'τύπου GroupBox, Panel, TabPage κλπ. 
                   'To DesignerAttribute αυτών των Controls προέρχεται από το Class System.Windows.Forms.ParentControlDesigner
                   Private Function IsContainer(ByVal ctl As Object) As Boolean
                               'Παίρνουμε το DesignerAttribute του Control
                               Dim des As System.ComponentModel.DesignerAttribute = _ 
                                           
    TypeDescriptor.GetAttributes(ctl).Item(GetType(System.ComponentModel.DesignerAttribute))
                               If des Is Nothing Then Exit Function
                               'Παίρνουμε το όνομα του τύπου του Designer, δημιουργούμε ένα System.Type
                               Dim Base As System.Type = System.Type.GetType(des.DesignerTypeName.ToString)
                               'και εξετάζουμε αν προέρχεται από το Class System.Windows.Forms.Design.ParentControlDesigner
                               Return Base.IsSubclassOf(GetType(System.Windows.Forms.Design.ParentControlDesigner))
                   End Function

             End Class

    End Class

    Πως το χρησιμποιούμε

    Μέσα στη φόρμα γράφουμε :

    Dim AllControls as MyControlsCollection = New MyControlsCollection(me)
    Οποτε χρειαστούμε το Controls Collection αναφερόμαστε στο property AllControls.Controls

    ή ακόμη καλύτερα

    Private _Controls as Collection
    Private Readonly Property AllControls() as MyControlsCollection
             Get 
                if
    _Controls is nothing then
                   Dim AllControls as MyControlsCollection = New MyControlsCollection(me)
                   _Controls = AllControls.Controls
                
                end if
                Return
    _Controls
             End Get   
    End Property

     

     

     

     

     


    Σταύρος Βαλσάμης
    Προγραμματιστής
  •  10-02-2006, 23:12 9528 σε απάντηση της 9525

    Απ: Μία Wrapper Class που μαζεύει όλα τα Controls μίας φόρμας

    Yes [Y] Ωραίο... Ταιριάζει με αυτά που λέγαμε εδώ http://www.dotnetzone.gr/cs/forums/8805/ShowPost.aspx
    Vir prudens non contra ventum mingit
  •  13-02-2006, 09:41 9595 σε απάντηση της 9525

    Απ: Μία Wrapper Class που μαζεύει όλα τα Controls μίας φόρμας

    Ωραίο!

    Μια άλλη εναλλακτική, στην οποία δε χρειάζεται να δημιουργείς νέα collections κάθε φορά που θέλεις να "περπατήσεις" όλα τα controls μιας φόρμας, είναι μια function που κάνει walk όλο το control tree και καλεί τον delegate που της έχεις περάσει για ένα ένα control στη σειρά.

    public delegate void ProcessControlDelegate(Control a_Control);

    public void EnumerateControls(Control.ControlCollection a_Controls, ProcessControlDelegate a_Delegate)
    {
     foreach (Control control in a_Controls)
     {
      a_Delegate(control);
      EnumerateControls(control.Controls, a_Delegate);
     }
    }

     

    Μπορείς να καλέσεις τον delegate μετά την αναδρομική κλήση της Enumerate controls αν θέλεις post order διάσχιση του δένδρου -- ή να το κάνεις παραμετρικό.

    Και τον καλείς:



    public void ShowControlName(Control a_Control)
    {
     MessageBox.Show(a_Control.Name);
    }

    ...

    EnumerateControls(this.Controls, new ProcessControlDelegate(ShowControlName));


    Νατάσα Μανουσοπούλου
  •  13-02-2006, 11:00 9597 σε απάντηση της 9595

    Απ: Μία Wrapper Class που μαζεύει όλα τα Controls μίας φόρμας

    Yes [Y] Πάρα πολύ σωστά!!!
    Θα μπορούσε και χωρίς την χρήση του Delegate, αλλά με χρήση Delegate μπορούμε να πάρουμε κι' άλλα properties με τον ίδιο enumarator.
    ΠΧ. ControlNames as String() ή ControlTags ή οτιδήποτε άλλο, κάνοντας την κλήση με άλλο method στο Delegate EnumerateControls(_Container.controls, New ProcessControlDelegate(AddressOf AddControlName))

    Η Class με την εναλλακτική λύση σε VB (ομολογώ πολύ καλύτερη και σωστότερη από την αρχική. Μπράβο mns).

    Public Class MyControlsCollection

             Private _Controls As Collection

             Private _Container As Object

             Public Delegate Sub ProcessControlDelegate(ByVal a_Control As Control)

             Public Sub New(ByVal Container As Object)

                      _Container = Container

             End Sub

             Public ReadOnly Property Controls() As Collection

                      Get

                            If _Controls Is Nothing Then

                               _Controls = New Collection

                               EnumerateControls(_Container.controls, New ProcessControlDelegate(AddressOf AddControl))

                            End If

                            Return _Controls

                      End Get

             End Property

             Public Sub EnumerateControls(ByVal a_Controls As Control.ControlCollection, ByVal a_Delegate As   ProcessControlDelegate)

           For Each control As Control In a_Controls

                   a_Delegate(control)

                   If IsContainer(control) AndAlso control.HasChildren Then

                      EnumerateControls(control.Controls, a_Delegate)

                   End If

             Next

             End Sub

             Private Sub AddControl(ByVal Control As Control)

                      _Controls.Add(Control, Control.Name)

            End Sub

             Private Function IsContainer(ByVal ctl As Object) As Boolean

                      Dim des As System.ComponentModel.DesignerAttribute = _

                      TypeDescriptor.GetAttributes(ctl).Item(GetType(System.ComponentModel.DesignerAttribute))

                      If des Is Nothing Then Exit Function

                            Dim Base As System.Type = System.Type.GetType(des.DesignerTypeName.ToString)

                            Return Base.IsSubclassOf(GetType(System.Windows.Forms.Design.ParentControlDesigner))

             End Function

    End Class


    Σταύρος Βαλσάμης
    Προγραμματιστής
  •  13-02-2006, 17:43 9617 σε απάντηση της 9597

    Απ: Μία Wrapper Class που μαζεύει όλα τα Controls μίας φόρμας

    Θα μου επιτρέψετε να διαφωνίσω με τα παραπάνω χωρίς όμως αυτό να σημαίνει πως δεν σέβομαι το χρόνο και τη μελέτη που αφιέρωσε ο/οι συνάδελφοι για να βγάλουν και αυτή την εναλλακτική λύση. Όμως ρε παιδιά προγραμματισμός είναι να γράφεις αυτό που θέλεις με λίγες γραμμές και έξυπνες γραμμές κώδικα. να χρησιμοποιείς όλες τις δυνατότητες του VS και να προχωράς... Αφού έχουμε το controls collection για κάθε φόρμα γιατί να καθόμαστε να εφεύρουμε τον τροχό από την αρχή; Δεν το καταλαβαίνω.

    Αν το παραπάνω αφορά μία άσκηση για να καταλάβουμε πως τυχόν μπορεί να υλοποιείται η control collection το δέχομαι. Αλλά αν το προτείνεται ως λύση για να χρησιμοποιούν οι υπόλοιποι τότε συγνώμη είμαι κάθετα αντίθετως.
  •  13-02-2006, 17:48 9618 σε απάντηση της 9595

    Απ: Μία Wrapper Class που μαζεύει όλα τα Controls μίας φόρμας

    Γιατί το ControlsCollection, αν το καλέσεις στη φόρμα θα σου επιστρέψει μόνο τα Controls που είναι πάνω σε αυτήν και όχι και αυτά που είναι σε κάποιον container πάνω στη φόρμα.
    Δηλαδή. Μέσα σε μία φόρμα βάλε ένα GroupBox και μέσα σε αυτό βάλε κάποιο Button.
    To From.Controls δεν θα περιλαμβάνει το button ενώ η λύση που κάναμε post θα το περιλαμβάνει.

    Σταύρος Βαλσάμης
    Προγραμματιστής
  •  13-02-2006, 18:03 9621 σε απάντηση της 9618

    Απ: Μία Wrapper Class που μαζεύει όλα τα Controls μίας φόρμας

    Private Sub ClearControlsText(ByVal theContainerControl As Control)
    Dim Ctrl As Control
    For Each Ctrl In theContainerControl.Controls
    Ctrl.Text = ""
    If Ctrl.HasChildren Then ClearControlsText(Ctrl)
    Next
    End Sub

    Αν γράψεις αυτό θα παίζει μία χαρά. Το παραπάνω θα σου αδείασει ακόμα και το text τον buttons αλλά μπορείς να κάνει ένα έλεγχο για το τι control έχουμε σε κάθε for each.
  •  13-02-2006, 18:41 9623 σε απάντηση της 9595

    Απ: Μία Wrapper Class που μαζεύει όλα τα Controls μίας φόρμας

    Xρειάζεται να έχω έλεγχο για κάθε control που δεν θέλω να μου καθαρίσει τον τύπο του (Button, Label, GroupBox, TabPage κλπ.).
    Και καλά να είναι τύπος που γνωρίζω για να το εξαιρέσω.
    Αν είναι κάποιο UserControl;
    Aν δεν θέλω να κάνω clearText αλλά θέλω να πάρω το Controls.Count;
    ΠΧ Φτιάξε ένα User control και βάλε μέσα ένα άλλο User Control ένα label και ένα TextBox και βάλτο μέσα σε ένα GroupBox μίας φόρμας.
    Στην περίπτωση που χρησιμοποιήσω την Wrapper Class το Controls.COunt θα είναι δύο (ένα το GroupBox και ένα to USer Control αδιάφορο του πόσα controls περιέχει αυτό), ενώ στην περίπτωση που υποδυκνείεις θα είναι πολύ περισσότερα.
    Για να το κάνω αυτό χωρίς το Class χρειάζομαι πολύ περισσότερο κώδικα από αυτόν του ίδιου του Class.
    Τώρα αν αυτό χρειάζεται σε πολλές φόρμες, με τον τρόπο που υποδεικνύεις χρειάζεται αυτόν το κώδικα να τον γράψω πολλές φορές σε πολλές φόρμες.
    Για τον λόγο αυτό, δημιουργούμε ένα Wrapper Class μία φορά και το καλούμε μέσα από την φόρμα με δύο γραμμές κώδικα και με εξασφαλισμένο το σωστό Controls Collection.


    Σταύρος Βαλσάμης
    Προγραμματιστής
  •  13-02-2006, 19:23 9625 σε απάντηση της 9623

    Απ: Μία Wrapper Class που μαζεύει όλα τα Controls μίας φόρμας

    Πιστεύω πως αυτή η συζήτηση θα συνεχίζεται για πολύ καιρό ακόμα με διάφορα επιχειρήματα. Άλλα σημαντικά και άλλα όχι. Μπορώ να αντικρούσω όλα τα παραπάνω που λες, όπως του ότι χρειάζεται να γράφεται σε κάθε φόρμα ή για το Count property. Νομίζω όμως πως δεν είναι αναγκαίο, απλά ήθελα να δείξω μία εναλλακτική και πιο απλή λύση για τους λόγους που έγραψα στο αρχικό μου post.

    Έχω ξαναγράψει και σε άλλο post πως τα παραδείγματα που γράφω είναι απλά για να μπορεί να καταλαβαίνει ο κάθε ένας το νόημα και απο εκεί και πέρα να βάλει τις δικές του παραλλαγές. Ας διαβάσει ο κάθε ενδιαφερόμενος και ας κρίνει μόνος του. Προσωπικά μου αρέσει ο απλός, έξυπνος και με λίγες γραμμές κώδικα πάντα.

  •  13-02-2006, 19:42 9628 σε απάντηση της 9625

    Απ: Μία Wrapper Class που μαζεύει όλα τα Controls μίας φόρμας

    Εκανες πάρα πολύ καλά infoCENTER, που έκανες τις παρατηρήσεις σου και έκανες post μία πολύ απλή και καλή λύση.
    Αλλωστε βρισκόμαστε εδώ ακριβώς γι' αυτό το λόγο.

    Σταύρος Βαλσάμης
    Προγραμματιστής
  •  13-02-2006, 20:01 9629 σε απάντηση της 9525

    Απ: Μία Wrapper Class που μαζεύει όλα τα Controls μίας φόρμας

    Code reuse = holy grail.

    Το να μπορείς να χρησιμοποιείς τον ίδιο κώδικα και αλλού υπό τη μορφή component μπορεί να σου λύσει τα χέρια, και να σου γλυτώσει αρκετό χρόνο.

    Το μόνο "πρόβλημα"; Έχουμε την τάση να προσθέτουμε συνεχώς καινούρια features στα components αυτά, με αποτέλεσμα πολλές φορές να καταλήγουμε σε λύσεις τόσο περίπλοκες, όσο και τα application blocks του .net community.
    Μην αφήνετε τα media να σας "ταΐζουν"!
  •  14-02-2006, 13:15 9661 σε απάντηση της 9625

    Απ: Μία Wrapper Class που μαζεύει όλα τα Controls μίας φόρμας

    Ο Walker που κάνει το Loop του InfoCENTER, δεν είναι και πολύ διαφορετικός από αυτόν του Class. Απλά το function αυτό (EnumarateControls) το βάλαμε σε ένα Class που έχει ένα property Controls Collection, έτσι ώστε μέσα σε κάθε φόρμα να μήν με απασχολεί να μαζέψω τα controls αλλά να τα διαχειριστώ ανάλογα με τις ανάγκες της φόρμας.

    Πιο κάτω  είναι μία πιο απλή εναλλακτική λύση, με ενσωμάτωση της πρότασης του InfoCENTER, μόνο που αντί να κάνω clear το text το βάζω στο Collection _Controls, ενώ κάνω walk μόνο τα Controls όπως GroupBox, Panel, TabPage κλπ.

    Public Class MyControlsCollection
    Private _Controls As Collection

    Public Sub New(ByVal Container As Object)
               _Controls = New Collection
               EnumerateControls(Container.Controls)
    End Sub

    Public ReadOnly Property Controls() As Collection
          Get
             Return _Controls
       End Get

    End Property

    Public Sub EnumerateControls(ByVal a_Controls As Control.ControlCollection)

             For Each control As Control In a_Controls
                   _Controls.Add(Control, Control.Name)
                   If IsContainer(control) AndAlso control.HasChildren Then
                      EnumerateControls(control.Controls)
                   End If
             Next

    End Sub

    Private Function IsContainer(ByVal ctl As Object) As Boolean

                Dim des As System.ComponentModel.DesignerAttribute = _
                TypeDescriptor.GetAttributes(ctl).Item(
    GetType(System.ComponentModel.DesignerAttribute))

                If des Is Nothing Then return false
                Dim Base As System.Type = System.Type.GetType(des.DesignerTypeName.ToString)
                Return Base.IsSubclassOf(GetType(System.Windows.Forms.Design.ParentControlDesigner))

    End Function

    End Class

    Αυτός κατά τη γνώμη μου είναι ο απλούστερος τρόπος να διαχειριστεί κανείς σωστά το Controls Collection ενός Container.

    ClearText:
    Κατά την άποψή μου ο σωστότερος (όχι όμως και ο απλούστερος) τρόπος  που εφαρμόζω στις δικές μου εφαρμογές για το Clear Text είναι ο εξής:

    Δημιουργείς ένα control Inherits TextBox ή οτιδήποτε άλλο (πχ third party) που θέλεις να καθαρίζεται και το μαρκάρεις με ένα δικό σου Attribute έστω myAttribute στο οποίο μπορείς να βάλεις οποιοδήποτε property ανάλογα με τις ανάγκες σου.
    Στο πιο πάνω Class βάζεις ένα function ClearText και στο loop ελέγχεις :

    if Not TypeDescriptor.GetAttributes(ctl).Item(GetType(MyAttribute)) is Nothing then
       ctl.text = ""
    end if

    Επειδή όμως μου αρέσει πολύ (και έχει αποδειχθεί πολύ χρήσιμο) να διατηρώ  Components όσο πιο core γίνεται, εγώ θα έκανα Overridable το EnumarateControls και θα δημιουργούσα ένα νέο Class (Inherits το MyControlsCollection).
    Μέσα σε αυτό το Class θα έκανα Override το EnumerateControls έτσι ώστε να μαζεύει μόνο τα Controls με το MyAttribute, και ένα Public method ClearControls. Στο Construction θα του πέρναγα το Controls.Collection του MyCOntrolsCollection, κάπως έτσι:

    Dim
    ClearTextControls = New InheritedMyCollection(AllControls)
    ClearTextControls.ClearText

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


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