Carousel-Control in Silverlight 2
Nichts leichter als das.
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: