How to Create a Silverlight Control Like the Telerik CoverFlow Control Part 2 – Code Behind

In the previous article, I showed you how to get styles and templates for the CoverFlow control. In this article, I will show how to make moving items.

Unfortunately, we can not define storyboards as in XAML becuase different animations must be applied to the item’s left and right side. So we must define several helper classes.

Class Animations to combine several DoubleAnimations:

private class Animations

{

    public DoubleAnimation LocalOffsetX { get; set; }

    public DoubleAnimation ScaleX { get; set; }

    public DoubleAnimation ScaleY { get; set; }

    public DoubleAnimation RotationY { get; set; }

}

 

Class AnimationService to recalculate animations for items.

private class AnimationService

{

    private const string offsetProperty = “(FrameworkElement.RenderTransform).(CompositeTransform.TranslateX)”;

    private const string scaleXProperty = “(FrameworkElement.RenderTransform).(CompositeTransform.ScaleX)”;

    private const string scaleYProperty = “(FrameworkElement.RenderTransform).(CompositeTransform.ScaleY)”;

    private const string rotationProperty = “(FrameworkElement.Projection).(PlaneProjection.RotationY)”;

    private Storyboard sb;

    private Dictionary<ListBoxItem, Animations> animations = newDictionary<ListBoxItem, Animations>();

    private LinkedList<ListBoxItem> listBoxItems = newLinkedList<ListBoxItem>();

    public double CenterX { get; set; }

    public double SelectedItemMargin { get; set; }

    public double ItemMargin { get; set; }

    public double Angle { get; set; }

    public double SelectedItemScale { get; set; }

    public double ItemScale { get; set; }

    public AnimationService(Storyboard sb)

{

        this.sb = sb;

SelectedItemMargin = 70;

ItemMargin = 60;

Angle = 45;

SelectedItemScale = 1;

ItemScale = 0.7;

}

    public void Reset()

{

sb.Stop();

sb.Children.Clear();

animations.Clear();

listBoxItems.Clear();

}

    public void Add(ListBoxItem element)

{

        Animations tmpAnimations = newAnimations();

animations.Add(element, tmpAnimations);

tmpAnimations.LocalOffsetX =new DoubleAnimation() { Duration = sb.Duration, To = 0 };

tmpAnimations.ScaleX =new DoubleAnimation() { Duration = sb.Duration, To = ItemScale };

tmpAnimations.ScaleY =new DoubleAnimation () { Duration = sb.Duration, To = ItemScale };

tmpAnimations.RotationY =new DoubleAnimation() { Duration = sb.Duration, To = 0 };

        Storyboard.SetTarget(tmpAnimations.LocalOffsetX, element);

        Storyboard.SetTargetProperty(tmpAnimations.LocalOffsetX, new PropertyPath(offsetProperty));

        Storyboard.SetTarget(tmpAnimations.ScaleX, element);

        Storyboard.SetTargetProperty(tmpAnimations.ScaleX, new PropertyPath(scaleXProperty));

        Storyboard.SetTarget(tmpAnimations.ScaleY, element);

        Storyboard.SetTargetProperty(tmpAnimations.ScaleY, new PropertyPath(scaleYProperty));

        Storyboard.SetTarget(tmpAnimations.RotationY, element);

        Storyboard.SetTargetProperty(tmpAnimations.RotationY, new PropertyPath(rotationProperty));

sb.Children.Add(tmpAnimations.LocalOffsetX);

sb.Children.Add(tmpAnimations.ScaleX);

sb.Children.Add(tmpAnimations.ScaleY);

sb.Children.Add(tmpAnimations.RotationY);

listBoxItems.AddLast(element);

}

    public Animations GetAnimations(ListBoxItemelement)

{

        return animations[element];

}

    public void Animate(ListBoxItem selectedItem)

{

animate(listBoxItems.Find(selectedItem));

}

    private void animate(LinkedListNode<ListBoxItem> selectedNode)

{

        if (selectedNode == null)

{

            return;

}

setDoubleAnimationsForSelectedNode(sb, selectedNode);

setDoubleAnimationsForNextNode(sb, selectedNode.Next, 1);

setDoubleAnimationsForPrevNode(sb, selectedNode.Previous, 1);

sb.Begin();

}

    private void setDoubleAnimationsForSelectedNode(Storyboard sb, LinkedListNode<ListBoxItem> selectedNode)

{

        Animations animation = animations[selectedNode.Value];

animation.LocalOffsetX.To = CenterX – selectedNode.Value.DesiredSize.Width / 2;

animation.ScaleX.To = SelectedItemScale;

animation.ScaleY.To = SelectedItemScale;

animation.RotationY.To = 0;

        Canvas.SetZIndex(selectedNode.Value, 0);

}

    private void setDoubleAnimationsForNextNode(Storyboard sb, LinkedListNode<ListBoxItem> nextNode, int index)

{

        if (nextNode == null)

{

            return;

}

        Animations animation = animations[nextNode.Value];

animation.LocalOffsetX.To = CenterX + SelectedItemMargin + ItemMargin * (index – 1);

animation.ScaleX.To = ItemScale;

animation.ScaleY.To = ItemScale;

animation.RotationY.To = Angle;

        Canvas.SetZIndex(nextNode.Value, 0 – index);

index++;

setDoubleAnimationsForNextNode(sb, nextNode.Next, index);

}

    private void setDoubleAnimationsForPrevNode(Storyboard sb, LinkedListNode<ListBoxItem> prevNode, int index)

{

        if (prevNode == null)

{

            return;

}

        Animations animation = animations[prevNode.Value];

animation.LocalOffsetX.To = CenterX – (SelectedItemMargin + prevNode.Value.DesiredSize.Width) – ItemMargin * (index – 1);

animation.ScaleX.To = ItemScale;

animation.ScaleY.To = ItemScale;

animation.RotationY.To = 0 – Angle;

        Canvas.SetZIndex(prevNode.Value, 0 – index);

index++;

setDoubleAnimationsForPrevNode(sb, prevNode.Previous, index);

}

}

 

Lets define several properties for customization CoverFlow control:

private AnimationService animationService;

private Dictionary<object, ListBoxItem> itemToListBoxItem = new Dictionary<object, ListBoxItem>();

public DependencyProperty SelectedItemMarginProperty = DependencyProperty.Register(“SelectedItemMargin”, typeof(Double), typeof(CoverFlow), new PropertyMetadata(new PropertyChangedCallback(SelectedItemMarginPropertyChangedCallback)));

public DependencyProperty ItemMarginProperty = DependencyProperty.Register(“ItemMargin”, typeof(Double), typeof(CoverFlow), new PropertyMetadata(new PropertyChangedCallback(ItemMarginPropertyChangedCallback)));

public DependencyProperty AngleProperty = DependencyProperty.Register(“Angle”, typeof(Double), typeof(CoverFlow), new PropertyMetadata(new PropertyChangedCallback(AnglePropertyChangedCallback)));

public DependencyProperty SelectedItemScaleProperty = DependencyProperty.Register(“SelectedItemScale”, typeof(Double), typeof(CoverFlow), new PropertyMetadata(new PropertyChangedCallback(SelectedItemScalePropertyChangedCallback)));

public DependencyProperty ItemScaleProperty = DependencyProperty.Register(“ItemScale”, typeof(Double), typeof(CoverFlow), new PropertyMetadata(new PropertyChangedCallback(ItemScalePropertyChangedCallback)));

public Double SelectedItemMargin

{

    get { return (Double)this.GetValue(SelectedItemMarginProperty); }

    set { this.SetValue(SelectedItemMarginProperty, value); }

}

public Double ItemMargin

{

    get { return (Double)this.GetValue(ItemMarginProperty); }

    set { this.SetValue(ItemMarginProperty, value); }

}

public Double Angle

{

    get { return (Double)this.GetValue(AngleProperty); }

    set { this.SetValue(AngleProperty, value); }

}

public Double SelectedItemScale

{

    get { return (Double)this.GetValue(SelectedItemScaleProperty); }

    set { this.SetValue(SelectedItemScaleProperty, value); }

}

public DoubleItemScale

{

    get { return (Double)this.GetValue(ItemScaleProperty); }

    set { this.SetValue(ItemScaleProperty, value); }

}

private static void SelectedItemMarginPropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)

{

    CoverFlow instance = d as CoverFlow;

instance.animationService.SelectedItemMargin = (Double)e.NewValue;

}

private static void ItemMarginPropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgse)

{

    CoverFlow instance = d as CoverFlow;

instance.animationService.ItemMargin = (Double)e.NewValue;

}

private static void AnglePropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)

{

    CoverFlow instance = d as CoverFlow;

instance.animationService.Angle = (Double)e.NewValue;

}

private static void SelectedItemScalePropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)

{

    CoverFlow instance = d as CoverFlow;

instance.animationService.SelectedItemScale = (Double)e.NewValue;

}

private static void ItemScalePropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)

{

    CoverFlow instance = d as CoverFlow;

instance.animationService.ItemScale = (Double)e.NewValue;

}

 

And now lets define CoverFlow constructor and ovverides several ListBox methods:

public CoverFlow()

{

    this.DefaultStyleKey = typeof(CoverFlow);

    this.SelectionChanged += coverFlowSelectionChanged;

    Storyboard sb = newStoryboard();

    this.Resources.Add(“sb”, sb);

sb.Duration = new TimeSpan(0, 0, 0, 0, 400);

animationService =new AnimationService(sb);

}

 

protected override Size ArrangeOverride(Size finalSize)

{

animationService.CenterX = (finalSize.Width / 2);

    if (this.SelectedItem != null)

{

animationService.Animate(itemToListBoxItem[this.SelectedItem]);

}

    this.Clip = newRectangleGeometry() { Rect = new Rect(0, 0, finalSize.Width, finalSize.Height) };

    return base.ArrangeOverride(finalSize);

}

 

protected override DependencyObject GetContainerForItemOverride()

{

    ListBoxItem result = base.GetContainerForItemOverride() as ListBoxItem;

result.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;

result.VerticalAlignment = System.Windows.VerticalAlignment.Center;

result.RenderTransformOrigin = new Point(0.5, 0.5);

result.RenderTransform =new CompositeTransform() { };

result.Projection =new PlaneProjection() { };

animationService.Add(result);

    return result;

}

 

protected override void PrepareContainerForItemOverride(DependencyObject element, object item)

{

    base.PrepareContainerForItemOverride(element, item);

itemToListBoxItem.Add(item, element as ListBoxItem);

}

 

protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e)

{

    base.OnItemsChanged(e);

animationService.Reset();

itemToListBoxItem.Clear();

}

 

void coverFlowSelectionChanged(object sender, SelectionChangedEventArgs e)

{

    if (e.AddedItems.Count <= 0 || itemToListBoxItem.Count <= 0)

{

        return;

}

    ListBoxItem element = itemToListBoxItem[e.AddedItems[0]];

animationService.Animate(element);

}

 

Thats all! Using:

<controls:CoverFlow ItemsSource=”{Binding Items}” SelectedItem=”{Binding SelectedItem, Mode=TwoWay}”

SelectedItemMargin=”60″ ItemMargin=”60″ Angle=”45″ SelectedItemScale=”1″ ItemScale=”0.6″ />

 

Looking for quality Silverlight Hosting? Look no further than Arvixe Web Hosting!

Tags: , , , , , | Posted under 3rd Party Software, Programming/Coding | RSS 2.0

Leave a Reply

Your email address will not be published. Required fields are marked *


6 + 2 =

You may use these HTML tags and attributes: <a href="" title="" rel=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>