Από τις πρώτες εκδόσεις του .NET Framework το PropertyGrid ήταν ένα από τα controls που πιστεύω ότι είχε κερδίσει τις εντυπώσεις. Κάνοντας assign ένα object στο PropertyGrid, μπορεί κάποιος να δει όλα του τα properties και να τα επεξεργαστεί. Το μαγευτικό είναι ότι οι αλλαγές που γίνονται στο PropertyGrid, περνάνε κατευθείαν στο object και οι αλλαγές αυτές να είναι ορατές κατά το run-time ενός προγράμματος. Αν και είχα τα παραπάνω μου ήταν γνωστά, δεν είχα βρεθεί σε ανάγκη να χρησιμοποιήσω το control για να εξυπηρετήσω την λειτουργικότητα μιας εφαρμογής.

MembershipSeeder

Όλα ξεκίνησαν όταν χρειάστηκα μία WinForm εφαρμογή, με την οποία θα μπορούσε κάποιος να διαχειριστεί τους χρήστες και του ρόλους του ASP.NET Membership. Προσπαθώντας να βρω κάτι σχετικό στο Internet, έπεσα επάνω στην εφαρμογή Membership Seeder του Forms Based Authentication (FBA), που αποτελεί μέλος του Community Kit for SharePoint. Πρέπει να πω ότι από την περιγραφή της εφαρμογής πίστεψα, ότι ήταν αυτό που ήθελα. Στην πραγματικότητα όμως, απογοητεύτηκα πάρα πολύ όταν είδα το interface (βλ. snapshot στα αριστερά).

Το interface που περίμενα να δω, ή πιο σωστά αυτό που είχα στο μυαλό μου ότι ήθελα να χρησιμοποιήσω, ήταν αυτό που υπάρχει μέσα στο Computer Management, για τους Local User and Groups. Έτσι πήρα την απόφαση να υλοποιήσω αυτό που ήταν στο μυαλό μου. Ξεκινώντας από αυτό που υπάρχει μέσα στο CodePlex, θα κέρδιζα χρόνο, γιατί ουσιαστικά αυτό που θα είχα να υλοποιήσω θα ήταν το interface, και θα χρειαζόταν να δημιουργήσω όλη την λογική.

UsersAndGroups

Κάτι δεν μου πήγε στην αρχή, όταν είδα τον κώδικα της εφαρμογής που ήταν γραμμένη σε VB.NET. Πρέπει να πω ότι με έπιασε η «κακία» μου να το ξαναγράψω σε C#, αλλά η αλήθεια ότι έχω γράψει περισσότερο κώδικα σε VB6, VBA, και VBScript, από όλο τον κώδικα που έχω γράψει σε C# πολλαπλασιασμένο επί 10. Οι παλιές συνήθειες δεν ξεχνιούνται και πρέπει να πω ότι σχετικά γρήγορα βγήκε το interface της εφαρμογής, χρησιμοποίησα τα ίδια εικονίδια με αυτά των Vista για τους Users και τα Groups, και η λειτουργικότητα θύμιζε πάρα πολύ, μέχρι που ήταν ίδια με του management console (βλ. snapshot στα δεξιά).

Όλα πήγαιναν κατ' ευχήν, και τα χαρακτηριστικά έβγαιναν από το ένα μετά το άλλο, στο βαθμό που μπορούσα να κάνω match one-to-one με τα χαρακτηριστικά του management console. Υπήρχαν χαρακτηριστικά του console που δεν υπάρχουν στο ASP.NET Membership, και το αντίθετο. Αλλά, ο νόμος του Murphy πάντα βρίσκει πεδίο να εφαρμοστεί: Ξαφνικά είχα την καλύτερη ιδέα, που μόνο και μόνο η υλοποίηση της θα άλλαζε την χρησιμότητα του project. Θα μπορούσα να χρησιμοποιήσω το PropertyGrid για να φτιάξω ένα interface για τις δυναμικές ιδιότητες του user profile του ASP.NET! Μιας και οι ιδιότητες είναι δυναμικές, θα είχα το τέλειο interface για να μπορούν να είναι δυνατή η επεξεργασία τους!

Ψάχνοντας την υπάρχοντα κώδικα του project, είδα ότι για μπορέσω να διαβάσω τις ιδιότητες του Profile για κάποιο χρήστη, θα έπρεπε να έχω ένα SettingsPropertyCollection object με τα ονόματα των ιδιοτήτων του profile, που θα το χρησιμοποιούσα σε συνδυασμό με ένα SettingsPropertyValueCollection object για να χρησιμοποιήσω για να «διαβάσω» τις τιμές των ιδιοτήτων. Δεν μπορώ να πω ότι ενθουσιάστηκα από το μηχανισμό που γινόταν ό χειρισμός των ιδιοτήτων, αλλά πίστεψα ότι αν τα συνδύαζα σε κάτι νέο, π.χ. ένα collection θα μπορούσα να τα εμφανίσω τα ζευγάρια των ονομάτων και τιμών των ιδιοτήτων στο PropertyGrid. Καλή η ιδέα μου, αλλά δε! Δεν δουλεύει έτσι το PropertyGrid.

Θα πρέπει το object που γίνεται assign στο PropertyGrid, να έχει properties τις ιδιότητες που θέλουμε να εμφανίσουμε, και οι τιμές που έχουν τα properties κατά την ώρα του run-time, θα εμφανιστούν σαν τιμές δίπλα στις ιδιότητες. Αν και το παραπάνω είναι απόλυτα κατανοητό για την χρήση με του PropertyGrid σε συνδιασμό με κάποιο control, ή object που θα θέλαμε να «παίξουμε» κατά το run-time, δεν να εφαρμοστεί εδώ, πόσο μάλλον με το collection που είχα σκοπό να υλοποιήσω, αλλά και με το γεγονός ότι οι ιδιότητες του ASP.NET Membership profile είναι σχεδόν μοναδικές σε κάθε υλοποίηση του ανά project.

Και λοιπόν; Θα έπρεπε να εγκαταλείψω το interface που ήθελα να υλοποιήσω; Διαβάζοντας περισσότερο για το PropertyGrid, αλλά και των συγγενών components που πρόσφερε το documentation, κατάλαβα ότι αφού δεν μπορούσα να έχω ένα object με τις ιδιότητες που έχω θα μπορούσα να φτιάξω ένα array από τις ιδιότητες που ήθελα και στην συνέχεια να το μετατρέψω δυναμικά σε object (χρήση του Reflection). Αν και η ιδέα ήταν καλή, η υλοποίηση της είχα κάποια ζητήματα: χρειαζόταν μεγάλη προσπάθεια σε συνδυασμό χρήσης του Reflection για να έχω το αποτέλεσμα. Σε την διαπίστωση του προγουμένου, συνειδητοποίησα ότι είχαν περάσει ήδη δύο ώρες από την στιγμή που είχα την «φοβερή» ιδέα, και κατάλαβα ότι ήταν καιρός να αρχίσω να κάνω αναζητήσεις στο Internet και να μην χάνω άλλο χρόνο.

Φαντάζεστε την απογοήτευση μου όταν διαπίστωσα ότι δεν μπορούσα να βρω σχετικό περιεχόμενο. Προσπαθούσα να θυμηθώ αν είχα δει εφαρμογές να χρησιμοποιούν το PropertyGrid για να κάνουν κάτι σχετικό - αλλά είμαι σίγουρος ότι εκτός από προγραμματιστικά περιβάλλοντα δεν το είχα δει κάπου αλλού. Βρήκα πολλές αναπάντητες ερωτήσεις, παρόμοιες με την δική μου, αλλά πουθενά μια απάντηση. Συνήθως όταν βλέπεις τέτοια αποτελέσματα στην αναζήτησή σου, συμπεραίνεις ή ότι θες να υλοποιήσεις κάτι που όλοι μπορούν να το κάνουν με «κλειστά μάτια», ή αυτό που θες είναι τόσο παράταιρο που μάλλον δεν μπαίνουν στον κόπο να σου απαντήσουν. Φιλοσοφώντας το τρόπο που δουλεύει το control σε συνδιασμό με το documentation, μάλλον ήμουν κοντύτερα στο δεύτερο. Μπορεί το interface του control να είναι όμορφο, αλλά δεν είναι δυνατόν να το χρησιμοποιήσω με τον τρόπο θα πίστευα ότι θα μου ήταν χρήσιμο.

Πρέπει να είχαν περάσει και 4 ώρες που παιδευόμουνα, όταν αποφάσισα να ψάξω να βρω ένα άλλο control που υλοποιούσε ένα ανάλογο interface, και να εγκαταλείψω την ιδέα να χρησιμοποιήσω το PropertyGrid. To interface είναι αρκετά ωραίο και οικείο για να μην έχει αντιγραφεί από control τρίτων για να μπορεί να χρησιμοποιηθεί με τον τρόπο που ήθελα. Και τη στιγμή που ήμουν έτοιμος να εγκαταλείψω την προσπάθεια τελείως, έγινε η αποκάλυψη: ένα άρθρο στο The Code Project, παρουσίαζε αυτό που ακριβώς χρειαζόμουν. Ο Tony Allowatt στο άρθρο του «Bending the .NET PropertyGrid to Your Will», αν και ήταν γραμμένο για .NET Framework v1.1, είχε υλοποιήσει ένα object που περιείχε ένα collection,  που μπορεί κάποιος να γεμίσει με τις ιδιότητες που κάποιος επιθυμούσε και να το περάσει στο PropertyGrid control. Βέβαια το object είχε event support, αλλά στην περίπτωση που μου δεν υπήρχε μια τέτοια απαίτηση.

Και ο κώδικας της επιτυχίας...

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 
Dim profileProperties As SettingsPropertyCollection = Nothing Dim profilePropertyValues As SettingsPropertyValueCollection = Nothing profileProperties = System.Web.Profile.DefaultProfile.Properties Dim currContext As SettingsContext = Nothing currContext = New SettingsContext() currContext("UserName") = value currContext("IsAuthenticated") = True profilePropertyValues = ProfileManager.Provider.GetPropertyValues(currContext, profileProperties) Dim profileAttributesCollection As PropertyTable profileAttributesCollection = New PropertyTable() For Each profileProperty As SettingsProperty In profileProperties Dim profileAttribute As New PropertySpec(profileProperty.Name, profileProperty.PropertyType, Nothing, Nothing, profileProperty.DefaultValue) profileAttributesCollection.Properties.Add(profileAttribute) profileAttributesCollection.Item(profileProperty.Name) = profilePropertyValues(profileProperty.Name).PropertyValue Next pgProfile.SelectedObject = profileAttributesCollection 

... μαζί με το interface που αλλάζει ανάλογα το implementation του profile:

UserProfile