Carousel-Control in Silverlight 2

Nichts leichter als das.

image

Da ich inzwischen einige Controls erstellt habe, arbeite ich ganz gerne mit Vererbung und meine Basisklasse für das Carousel ist der BaseContainer:

    1: using System.Windows.Controls;
    2: using System.Windows;
    3:  
    4: namespace Oliver.Controls
    5: {
    6:     public partial class BaseContainer : Panel
    7:     {
    8:         #region ItemWidth
    9:  
   10:         public double ItemWidth
   11:         {
   12:             get { return (double)GetValue(ItemWidthProperty); }
   13:             set { SetValue(ItemWidthProperty, value); }
   14:         }
   15:  
   16:         public static readonly DependencyProperty ItemWidthProperty =
   17:             DependencyProperty.Register("ItemWidth", typeof(double), typeof(BaseContainer), 
   18:             new PropertyMetadata(100.0, OnItemWidthChanged));
   19:  
   20:         private static void OnItemWidthChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
   21:         {
   22:             BaseContainer baseContainer = d as BaseContainer;
   23:             baseContainer.InvalidateArrange();
   24:         }
   25:  
   26:         #endregion 
   27:  
   28:         #region ItemHeight
   29:  
   30:         public double ItemHeight
   31:         {
   32:             get { return (double)GetValue(ItemHeightProperty); }
   33:             set { SetValue(ItemHeightProperty, value); }
   34:         }
   35:  
   36:         public static readonly DependencyProperty ItemHeightProperty =
   37:             DependencyProperty.Register("ItemHeight", typeof(double), typeof(BaseContainer),
   38:             new PropertyMetadata(100.0, OnItemHeightChanged));
   39:  
   40:         private static void OnItemHeightChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
   41:         {
   42:             BaseContainer baseContainer = d as BaseContainer;
   43:             baseContainer.InvalidateArrange();
   44:         }
   45:  
   46:         #endregion
   47:  
   48:         
   49:     }
   50: }

Die Datei BaseContainer.cs

Sie enthält zwei Eigenschaften “ItemWidth” und “ItemHeight” die ich in allen Container-Controls immer wieder benötigte, daher kommen diese in die “Masterklasse”.

In Wirklichkeit ist dies eine WPF-Klasse die ich in meinen Silverlight-Controls ebenfalls verwende und als Referenz einbinde. Für gewisse Effekte benötige ich dann auch noch immer wieder gerne die aktuelle Framerate des Plug-Ins. Diese Funktion kann allerdings WPF nicht auf meine gewünschte Art und Weise. Deshalb erweitere ich die BaseContainer-Klasse mittels Partial Class um ein Feature:

    1: using System;
    2: using System.Windows;
    3: using System.Windows.Controls;
    4:  
    5: namespace Oliver.Controls
    6: {
    7:     public partial class BaseContainer : Panel
    8:     {
    9:         public TimeSpan FrameRate
   10:         {
   11:             get 
   12:             {
   13:                 return GetFrameRate();
   14:             }
   15:         }
   16:  
   17:         public static int FramesPerSecond
   18:         {
   19:             get { return 1000 / Application.Current.Host.Settings.MaxFrameRate; }
   20:         }
   21:  
   22:         public static TimeSpan GetFrameRate()
   23:         {
   24:             int ms = FramesPerSecond;
   25:             return TimeSpan.FromMilliseconds(ms); 
   26:         }
   27:     }
   28: }

Die Datei BaseContainer.Silverlight.cs

Das eigentliche Logik für das Carousel kommt in die Carousel.cs-Datei.

Dabei sind zwei Methoden besonders wichtig: MeasureOverride und ArrangeOverride

MeasureOverride –> Diese sagt jedem Element im Panel, das sich die “Kinder” des Elements an die neue Größe anzupassen haben …

    1: protected override Size MeasureOverride(Size availableSize)
    2: {
    3:     foreach (UIElement element in Children)
    4:     {
    5:         element.Measure(new Size(ItemWidth, ItemHeight));
    6:     }
    7:     return availableSize;
    8: }

ArrangeOverride –> Ordnet die Elemente im Panel neu an. In unserem Fall, wird der Winkel der Elemente berechnet, an dem die einzelnen Elemente stehen.

    1: protected override Size ArrangeOverride(System.Windows.Size finalSize)
    2: {
    3:     for (int i = 0; i < Children.Count; i++)
    4:     {
    5:         // Calculate and set angle of all children
    6:         double angle = i * (Math.PI * 2) / Children.Count;
    7:         Children[i].SetValue(CarouselPanel.AngleProperty, angle);
    8:     }
    9:     DoArrange();
   10:  
   11:     return base.ArrangeOverride(finalSize);
   12: }

Die Methode DoArrange() sorgt dafür das sich das ganze auch noch drehen kann.

    1: void DoArrange()
    2: {
    3:     double height = this.DesiredSize.Height;
    4:     double width = this.DesiredSize.Width;
    5:  
    6:     Point center = new Point((width - ItemWidth) / 2, (height - ItemHeight) / 2);
    7:     double radiusX = center.X;
    8:     double radiusY = center.Y; 
    9:     double scale = ScalePerspective; 
   10:  
   11:     for (int i = 0; i < Children.Count; i++)
   12:     {
   13:         UIElement item = Children[i];
   14:  
   15:         double current = (double)item.GetValue(CarouselPanel.AngleProperty);
   16:         double angle = current + (_internalSpeed * (3 / 360.0) * (2 * Math.PI));
   17:         item.SetValue(CarouselPanel.AngleProperty, angle);
   18:         double radians = angle;
   19:  
   20:         Point p = new Point(
   21:             (Math.Cos(radians) * radiusX) + center.X,
   22:             (Math.Sin(radians) * radiusY) + center.Y);
   23:     
   24:         if (item.RenderTransform == null || 
   25:             (!(item.RenderTransform is ScaleTransform)))
   26:         {
   27:             item.RenderTransform = new ScaleTransform();
   28:         }
   29:         
   30:         ScaleTransform sc = item.RenderTransform as ScaleTransform;
   31:  
   32:         double scaleMinusRounding  = p.Y / (center.Y + radiusY); 
   33:         sc.ScaleX = sc.ScaleY =  Math.Min(scaleMinusRounding + scale, 1.0) ; 
   34:  
   35:         int zIndex = (int) ((p.Y / this.Height) * 50);                   
   36:         item.SetValue(Canvas.ZIndexProperty, zIndex);
   37:  
   38:         Rect r = new Rect(p.X, p.Y, ItemWidth, ItemHeight);
   39:         item.Arrange(r);
   40:         item.InvalidateArrange();
   41:      }
   42: }

Den vollständigen Code kann man unter www.the-oliver.com herunterladen.

Das Ergebnis sieht wie folgt aus:

Get Microsoft Silverlight