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

Silverlight Αttached Βehavior: Φτιάχνοντας ένα Marquee TextBox Control

Έχω ένα Grid στο Silverlight Application και θέλω να κάνω ένα marquee textbox (autoscroll) από δεξιά προς τα αριστερά, μέσα στο κελί του Grid. Τι χρειάζεται να κάνω για να προσθέσω σε ένα control αυτή τη λειτουργία;

Για αρχή έχουμε ένα textbox και στα γρήγορα κάνουμε ένα Translate RenderTransform αλλάζοντας την τιμή, στον άξονα των x από θετικές σε αρνητικές τιμές.

<TextBlock Grid.Row="0" Grid.Column="0" Foreground="#FF2755AF"            FontSize="13.333" x:Name="ScrollText" Text="Really Really Really Really Really Really Large Text" >     <TextBlock.RenderTransform>         <TranslateTransform x:Name="translate" />     </TextBlock.RenderTransform>     <TextBlock.Triggers>         <EventTrigger RoutedEvent="Grid.Loaded">             <BeginStoryboard>                 <Storyboard RepeatBehavior="Forever">                     <DoubleAnimation                         From="400" To="-400"                         Storyboard.TargetName="translate"                         Storyboard.TargetProperty="X"                         Duration="0:0:20" />                 </Storyboard>             </BeginStoryboard>         </EventTrigger>     </TextBlock.Triggers> </TextBlock>

Δυστυχώς όμως έχουμε πολλά προβλήματα όπως φαίνεται. Το κείμενο εκτός του ότι φεύγει τελείως εκτός των bounds του Grid, γιατί έγινε βίαιο translate χωρίς φόβο και πάθος, είναι και clipped ως προς το content. Αν παρατηρείτε μάλιστα, είναι clipped στο μέγεθος του κελιού (0,0). Αυτό γίνεται γιατί το Grid, πριν απεικονίσει τα αντικείμενα που έχει στο visual tree, τα κάνει clip, αυτόματα.

image

Το πρώτο πρόβλημα μπορούμε να το χειριστούμε με clipping στο Grid. Το δευτερο όμως όχι.

<Grid.Clip>     <RectangleGeometry Rect="0,0,100,20"/> </Grid.Clip>

Οπότε θέλουμε να κάνουμε override το clipping του Grid, όπως και τo clipping του textbox. Ο Canvas μας βοηθάει και στις δύο περιπτώσεις, γιατί αφενός αφήνει ελεύθερο το Actual Size του textbox και αφετέρου έχει και αυτό Clipping dependency property ως UIElement.

<Canvas >     <Canvas.Clip>         <RectangleGeometry Rect="0,0,100,20"/>     </Canvas.Clip>     <TextBlock Grid.Row="0" Grid.Column="0" Foreground="#FF2755AF"                 FontSize="13.333" x:Name="ScrollText" Text="Really Really Really Really Really Really Large Text" >     <TextBlock.RenderTransform>         <TranslateTransform x:Name="translate" />     </TextBlock.RenderTransform>     <TextBlock.Triggers>         <EventTrigger RoutedEvent="Grid.Loaded">             <BeginStoryboard>                 <Storyboard RepeatBehavior="Forever">                     <DoubleAnimation                         From="300" To="-300"                         Storyboard.TargetName="translate"                         Storyboard.TargetProperty="X"                         Duration="0:0:20" />                 </Storyboard>             </BeginStoryboard>         </EventTrigger>     </TextBlock.Triggers>     </TextBlock> </Canvas>

Δυστυχώς όμως έχουμε και εδώ μερικά προβλήματα. Καρφωτές τιμές στο Rect και στο From / To του δεν μπορούν να συνδυαστούν με ένα δυναμικό μέγεθος του Text, οπότε για να γίνει το control λίγο reusable χρειάζεται λίγο refactoring.

Η βασική ιδέα είναι να υπολογίζουμε δυναμικά κάποια πράγματα, για να μην χρειάζεται να βάλουμε καρφωτές τιμές. Μπορούμε να το κάνουμε αυτό με ένα πολύ απλό pattern που λέγεται attached behavior. Για να μπορούμε από ένα κεντρικό σημείο να αλλάξουμε ιδιότητες του TextBlock χωρίς να το κάνουμε extend, χρησιμοποιούμε attached property. Ορίζουμε λοιπόν ένα attached property κάπου (στη main page σε εμάς, σε ένα behavior σε άλλες περιπτώσεις), δεν έχει σημασία, για το demo μας, το οποίο θα το δηλώσουμε στο textblock που μας ενδιαφέρει. Θυμίζουμε ότι attached properties είναι properties που ορίζονται σε κάποια κλάση και μπορούν να χρησιμοποιηθούν από άλλες. Τυπικό παράδειγμα το Grid.Row και Grid.Column που βάζουμε στα elements, που περιέχονται σε Grid Panel.

#region MakeScrollable Property public static void SetMakeScrollable(UIElement element, bool value) {     element.SetValue(MakeScrollableProperty, value); } public static bool GetMakeScrollable(UIElement element) {     return (Boolean)element.GetValue(MakeScrollableProperty); } public static readonly DependencyProperty MakeScrollableProperty =     DependencyProperty.RegisterAttached(         "MakeScrollable", typeof (bool), typeof (MainPage),         new PropertyMetadata(false, OnMakeScrollableChanged)); private static void OnMakeScrollableChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {     FrameworkElement me = d as FrameworkElement;     me.Loaded += me_Loaded; } static void me_Loaded(object sender, RoutedEventArgs e) {     Clip(sender); } private static void Clip(object sender) {     FrameworkElement me = (FrameworkElement)sender; //Textblock size changed     FrameworkElement parent = (FrameworkElement)me.Parent;     if (GetMakeScrollable(me))     {         parent.Dispatcher.BeginInvoke(()=>         {             parent.Clip = new RectangleGeometry { Rect = new Rect(0, 0, parent.ActualWidth, parent.ActualHeight) };             var translateTransform = new TranslateTransform();             translateTransform.SetValue(NameProperty, "translate");             me.RenderTransform = translateTransform;             Storyboard storyboard = new Storyboard() { RepeatBehavior = RepeatBehavior.Forever};             DoubleAnimation doubleAnimation = new DoubleAnimation()             {                 From = parent.ActualWidth, To = -(me.ActualWidth), Duration = new Duration(new TimeSpan(0, 0, 20))             };             doubleAnimation.SetValue(Storyboard.TargetNameProperty, "translate");             doubleAnimation.SetValue(Storyboard.TargetPropertyProperty,  new PropertyPath("(TranslateTransform.X)"));             storyboard.Children.Add(doubleAnimation);             me.Resources.Add("StoryBoard", storyboard);             storyboard.Begin();         });     } } #endregion

Στη δική μας περίπτωση, ορίζουμε το MakeScrollableProperty. Όταν τεθεί από το textblock, αυτό καλεί την αντίστοιχα ορισμένη OnMakeScrollableChanged που στην ουσία κάνει το clipping.

<Canvas Grid.Row="0" Grid.Column="0" >     <TextBlock SilverlightApplication2:MainPage.MakeScrollable="true"                Text="Really Really Really Really Really Really Large Text "/> </Canvas>

image

Σίγουρα μπορούμε να κάνουμε ακόμα πιο κομψό τo “behavior”, διαχειριζόμενοι και τον canva, αλλά νομίζω ότι ήδη είναι αρκετά reusable για να χρησιμοποιηθεί στα πλαίσια ενός μικρού project. Τέλος, κρατήστε τη βασική ιδέα πίσω από το attached behavior. Αποκτούμε reference στο control που μας ενδιαφέρει μέσα από attached property, κάνοντας hooks και οτιδήποτε, μέσα από PropertyMetadata, αφήνοντας την προσθήκη λειτουργικότητας να γίνει δηλωτικά.

Smile

Posted: Τετάρτη, 20 Οκτωβρίου 2010 4:23 πμ από το μέλος George J. Capnias
Δημοσίευση στην κατηγορία: , ,

Σχόλια:

Χωρίς Σχόλια

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

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

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

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

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

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

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

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