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

A note to self: Silverlight DataBinding awesomeness μέσα από Expression Blend, XAML και κώδικα

 Post-it notes for la patilla

 

Το πιο δυνατό στοιχείο του Silverlight είναι η εκφραστικότητα των data binding μηχανισμών του με όλες τις μορφές που μας το παρέχει. Separation of concerns, animations, visual states, MVVM, αλληλένδετα controls και πολλά ακόμα “βαριά” ή ελαφρά features οφείλουν την ύπαρξή τους στο Data Binding.

Τι είναι Data Binding;

Data Binding είναι η σύνδεση μεταξύ μίας πηγής δεδομένων και ενός προορισμού.

Στο silverlight η πηγή είναι ένα CLR αντικείμενο που διατηρεί, ανανεώνει, κτλ πληροφορία (ένα object συγκεκριμένου τύπου) και ο προορισμός είναι ένα SL control. Όταν όλα είναι δηλωμένα σωστά, τότε όταν το CLR αντικείμενο αλλάζει με κάποιο τρόπο, μπορει να παρέχει ειδοποιήσεις για το τι άλλαξε, ποιό CLR property δηλαδή και όσα SL controls (elements) έχουν γίνει databound εκεί, μπορούν να ανανεώσουν τις τιμές τους. Η πρώτη επαφή με το DataBinding συνήθως έρχεται μέσα από τη XAML.

Through Expression Blend

Ας δούμε ένα μικρό παράδειγμα πριν πούμε τα βασικά του DataBinding και μάλιστα μέσα από το Blend, , ώστε να το γνωρίσουμε και αυτό το πολύτιμο εργαλείο καλύτερα. Έστω ότι θέλω να φτιάξω μία εφαρμογή για rating ταινιών (SL-IMDB δηλαδή), γρήγορα, τακτοποιημένα, μέσα από το Blend. Θέλω τα δεδομένα μου στην οθόνη να απεικονίζονται με βάση κάποιο συγκεκριμένο CLR Object, το οποίο μου έρχεται από τη βάση δεδομένων. Φτιάχνω ένα νέο silverlight project και για αρχή το object μου. Add New Item και φτιάχνω μία κλάση movie, που την γράφω κατευθείαν μέσα από το Blend έχοντας κανονικά Intellisense. Όχι ότι θα αναπτύξω κώδικα για το SL project στο Blend, αλλά μας δίνεται αυτή η ευκολία, χωρίς να χρειαζόμαστε πάντα και VS Studio και Expression Blend αναγκαστικά για κάποια μικροαλλαγή. Στη συνέχεια φτιάχνω μία καινούρια Page, ας χρησιμοποιήσουμε την ήδη υπάρχουσα MainPage.xaml. Ας δημιουργήσουμε και Sample Data στα γρήγορα για να βλέπουμε τη φτιάχνουμε.

Πηγαίνουμε δεξιά, πατάμε τη καρτέλα Data και Create διαλέγοντας την κλάση που θα κάνουμε populate με τυχαία (τα κλασσικά λατινικά) δεδομένα.

image

imageimageΜετά κάνοντας Drag το Movie στο Layout Root, έχω στο DataContext μου τυχαία δεδομένα, να κοιτάζω. Έχω τρεις περιοχές στην παραπάνω εικόνα. Έναν τίτλο (πάνω αριστερά), rating, περιγραφή και μία εικόνα, κάτω από τον τίτλο, που δεν έγινε populate με sample data, καθώς ο τύπος είναι Uri και το Sample Data από μόνο του δεν παρήγαγε κάποιο Url για να δείξει. Έχοντας το DataContext μου για τη σελίδα ορισμένο σε ένα Movie CLR object, το Expression Blend μπορεί να μου δώσει την δυνατότητα να επιλέξω γραφικά ποιό control θα παίρνει από που, δεδομένα. Επιλέγω το Textbox λοιπόν και πατάω δεξιά στα common properties το Text. Τι θέλω; Να γεμίζει με δεδομένα από την κλάση μου, οπότε πηγαίνω στα advanced properties και επιλέγω το DataContext tab. Εκεί θα βρω τα properties από τη Movie Class μου, και επιλέγω το Title. Κάνω το ίδιο και για τα υπόλοιπα. Στο Rating όμως θέλω και κάτι ακόμα. Όπως το Imdb. Να δείχνει μεν το μέσο όρο αλλά να μπορώ και εγώ να επιλέξω τιμή που αργότερα (θα συνυπολογιστεί στο ολικό μέσο όρο). Οπότε έχουμε μία διαφοροποίηση. Δεν θέλω η τιμή του να έρχεται στο UI μόνο, αλλά να επιλέξω κάτι εγώ και να κληθεί ο setter στο αντίστοιχο property επίσης. Επιλέγουμε λοιπόν το mode και το θέτουμε σε Two Way, όπως φαίνεται στο screenshot. Το μόνο που μένει για το demo μας είναι να θέσουμε με κάποιο τρόπο το DataContext της σελίδας μας σε ένα νέο Movie instance. Αυτό συνήθως το διαχειρίζεται το MVVM καλά, αλλά για την περίσταση μας αρκεί στο construction της σελίδας να γράψουμε ένα

 

public MainPage()         {             // Required to initialize variables             InitializeComponent();             this.DataContext = new Movie             {                 Title = "The Hustler",                 Rating = 0.85,                 Description = "The Hustler is a 1961 American drama film directed by Robert Rossen from the 1959 novel of the same name he and Sidney Carroll adapted for the screen. It tells the story of small-time pool hustler Fast Eddie. Felson and his desire to prove himself the best player in the country by beating legendary pool player Minnesota Fats. After initially losing to Fats and getting involved with unscrupulous manager Bert Gordon, Eddie returns to beat Fats, but only after paying a terrible personal price. The film was shot on location in New York City. It stars Paul Newman as Eddie Felson, Jackie Gleason as Minnesota Fats, Piper Laurie as Sarah, and George C. Scott as Bert.",                 Poster = new Uri("http://i23.photobucket.com/albums/b352/grnemo/the-hustler-paul-newman-jackie-gleason1.jpg")             };         }

Το αποτέλεσμα είναι το παρακάτω, έχοντας πάρει τις τιμές για το Silverlight application μας από κάποιο object του model μας. Ανοίγοντας το project από το Visual Studio, μας ζητάει να ενεργοποιήσουμε στο web config το debugging. Ανοίγουμε την κλάση μας, βαζουμε ένα breakpoint στον setter στο Rating και πατάμε τη δική μας βαθμολογία. Βλέπουμε ότι καλείται ο setter με τη νέα τιμή.

image  image

Έστω ότι θέλω να έχω μία λίστα από ταινίες, ή αντικείμενα και να βλέπω την περιγραφή τους σε παρόμοιο layout. Το DataBinding από την έκδοση 3 του silverlight έχει ένα feature με το οποίο μπορούμε να συνδέσουμε, controls μεταξύ τους, με καταγραφή μόνο των controls που έχουν κάποια τιμή που μας ενδιαφέρει. Θέλουμε λοιπόν, όποτε αλλάζουμε το SelectedItem από τη λίστα, να αλλάζουμε properties σε άλλα controls. Για τις ανάγκες του demo, έφτιαξα ένα demo datasource από το ίδιο menu όπως και πριν (ορίζοντας τον τύπο των dummy δεδομένων μου, ακόμα και της εικόνας), έφτιαξα ένα ListBox και έκανα drag and drop το property name, από το Data Context παράθυρο στο ListBox που ήθελα, όπως φαίνεται στην εικόνα. Στα επιμέρους controls, έβαλα bindings από το Expression Blend, με το ίδιο τρόπο. Επιλέγοντας την τελίτσα δεξιά από το property του κάθε control στο common properties, διαλέγοντας αυτή τη φορά, το tab, Element Property, επιλέγοντας το scene element που με ενδιαφέρει και ορίζοντας το property στο custom path expression. To αποτέλεσμα φαίνεται στην παρακάτω εικόνα.

imageimage

Revealing XAML

Αφού είχαμε το πρώτο μας Demo στο expression blend, ας δούμε τι γίνεται στη XAML που παράγεται από κάτω. Η αλήθεια είναι ότι αρκετές φορές όταν σχεδιάζεται μία σελίδα θα χρειαστεί να γράψετε αυτούσια XAML με το χέρι. Η XAML ορίζει τo λεγόμενo binding markup extension, όπου για το Silverlight (στην τρέχουσα version, 4)  είναι ένα από τα, Binding, StaticResource, TemplateBinding, RelativeSource. Εμάς μας ενδιαφέρει εκείνο το extension που λέγεται binding. Όπως κάθε extension έτσι και το Binding ορίζεται μέσα σε “{” “}” και προσδιορίζει το binding expression μεταξύ εκείνου του Dependency property και του property κάποιου CLR Object. Στην εφαρμογή μας ο τίτλος, η περιγραφή, το poster και το rating έχουν την παρακάτω απεικόνιση σε XAML:

      <TextBlock Text="{Binding Title}" …. />       <TextBlock Text="{Binding Description}" …. />       <Image>                 <Image.Source>                     <BitmapImage UriSource="{Binding Poster}"/>                 </Image.Source>       </Image>       <toolkit:Rating ItemCount="10" Value="{Binding Rating, Mode=TwoWay}"  />

Στo rating, που θέλαμε, όχι μόνο να διαβάζουμε τιμές από το object και να τις απεικονίζουμε, αλλά και να θέτουμε τιμές, πρέπει να δηλώσουμε ότι το DataBinding αφορά και data entry. Αυτό γίνεται με το Mode=TwoWay.

Στη συνέχεια μετασχηματίσαμε το projectaki ώστε να υποστηρίζει selection από listbox και τα αντίστοιχα controls να γεμίζουν πληροφορία, από το Data Context του selected item του Listbox. Το Binding expression είναι διαφορετικό ως εξής. Ας πάρουμε το Image Control για την απεικόνιση του Poster. Δηλώνουμε το element με ElementName και μετά το Path του property που μας ενδιαφέρει. Όμοια και για τα υπόλοιπα.

<Image Margin="236,110,258,178" Source="{Binding  ElementName=MovieList, Path=SelectedItem.Poster}"/>

Το ElementName, το Path και το Mode είναι τρία από τα features του Binding. Ακολουθεί μία λίστα και σύντομη περιγραφή από τα πιο συχνά χρησιμοποιούμενα. Μπορούν αν χρησιμοποιηθούν χωριζόμενα με κόμμα.

Property Περιγραφή
Path To property path που μας ενδιαφέρει από το object που γίνεται bound.
Converter Ένα object που αλλάζει την τιμή ενός object σε μία άλλη (συμβατή με το target dependency property). Υλοποιεί το IValueConverter. Για παράδειγμα. BooleanToVisibility, ή UrlToImage.
ElementName Για databinding μεταξύ elements.
FallbackValue Default τιμή για εμφάνιση αν αποτύχει το data binding.
RelativeSource Πραγματοποιείται bind, με object που βρίσκεται σε μία σχετική θέση (ιεραρχίας) με το target object.
StringFormat Για παραμετροποιήσιμη εμφάνιση των strings.
Source Χρήση όταν θέλουμε να κάνουμε bind με κάτι που δεν βρίσκεται στο DataContext. Για παράδειγμα,  {Binding Source={x:Static DateTime.Now}, Path=Day}

Αξίζει σε αυτό το σημείο να σημειώσουμε κάποια πράγματα. Για αρχή, εξ ορισμού το silverlight θα πετάξει exception αν χρησιμοποιηθούν δύο ή περισσότερα ταυτόχρονα, των Source, RelativeSource και ElementName, για ευνόητους λόγους.

Αξίζει να πούμε ένα παράδεγμα για τον πολύ χρήσιμο Converter.  O converter είναι ένα object που υλοποιεί δύο συναρτήσεις, μία για conversion του source object σε κάτι άλλο και μία την ανάποδη διαδικασία. Έστω ότι θέλουμε να χρησιμοποιήσουμε ένα string formatter έτσι ώστε, όταν έρχεται ένα string object από το source να μετασχηματίζεται σε κάτι άλλο.

<StackPanel> <HyperLink NavigateUri=”{Binding Uri, Converter={StaticResource StringToUriConverter}, ConverterParameter=’{0:d}’}“ /> </StackPanel>

Υλοποιούμε τον converter και τον κάνουμε reference ως static resource στο XAML μας, χρησιμοποιώντας το κανονικά.

public class StringToUriConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { return new Uri(value as string); } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new NotImplementedException(); } }

Ένα πολύ ωραίο trick όταν βλέπετε τι σας έρχεται από τον binding μηχανισμό στο debug, είναι να κάνετε έναν DebugConverter, υλοποιώντας τον και κάνοντας ότι θέλετε μέσα του, ή απλά να τον έχετε για το πολύτιμo brakepoint που μπορείτε να βάλετε για να κάνετε inspect το value.

Code

Όταν γράφουμε στη XAML το binding, τότε στη πραγματικότητα το Silverlight δημιουργεί ένα instance της κλάσης Binding που ορίζεται στο System.Windows.Data και πραγματοποιεί αυτό ακριβώς το δέσιμο. Μας δίδεται και εμάς η δυνατότητα να πραγματοποιήσουμε δικά μας bindings in code .

<txtEcho Text="{Binding}" .../>

Binding myBinding = new Binding("Text"); myBinding.Source = txtRealBox;   txtEcho.SetBinding(System.Windows.Controls.TextBox.TextProperty, myBinding);

Η βασική προϋπόθεση για να παίξει το binding είναι η κλάση που μπαίνει στο datacontext να υλοποιεί το

public interface INotifyPropertyChanged {     event PropertyChangedEventHandler PropertyChanged; }

Και τα properties που γίνονται bound να κάνουν Raise όταν αυτά γίνονται set (o untyped τρόπος με magic-string στο propertyName).

public void NotifyPropertyChanged(string propertyName)     {         if (PropertyChanged != null)         {             PropertyChanged(this,                 new PropertyChangedEventArgs(propertyName));         }     }

To blog post για το MVVM (μία custom έκδοση δλδ, όπου το ViewModel γνώριζε το View - [Μαθαίνοντας Design Patterns] Model – View – ViewModel) κάνει χρήση αυτών των μηχανισμών.

Ας κλείσω με μία τυπική PIC από το msdn, που τα λέει όλα!

Posted: Δευτέρα, 18 Οκτωβρίου 2010 11:05 πμ από το μέλος George J. Capnias
Δημοσίευση στην κατηγορία: , ,

Σχόλια:

Χωρίς Σχόλια

Ποιά είναι η άποψή σας για την παραπάνω δημοσίευση;

(απαιτούμενο)

(απαιτούμενο)

(προαιρετικό)

(απαιτούμενο)
ÅéóÜãåôå ôïí êùäéêü:
CAPTCHA Image

Ενημέρωση για Σχόλια

Αν θα θέλατε να λαμβάνετε ένα e-mail όταν γίνονται ανανεώσεις στο περιεχόμενο αυτής της δημοσίευσης, παρακαλούμε γίνετε συνδρομητής εδώ

Παραμείνετε ενήμεροι στα τελευταία σχόλια με την χρήση του αγαπημένου σας RSS Aggregator και συνδρομή στη Τροφοδοσία RSS με σχόλια