Click here to Skip to main content
15,896,557 members
Articles / Desktop Programming / WPF

WPF: Carousel Control

,
Rate me:
Please Sign up or sign in to vote.
4.95/5 (84 votes)
18 Apr 2011CPOL17 min read 324.7K   13.5K   148  
Highly customisable WPF Carousel control.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Collections;
using System.Windows.Media.Animation;
using Expression.Samples.PathListBoxUtils;
using System.Windows.Interactivity;

using Carousel.Helpers;
using System.ComponentModel;
using Microsoft.Expression.Controls;
using System.Windows.Automation.Peers;
using System.Windows.Automation.Provider;
using System.Diagnostics;


namespace Carousel
{
    public enum PathType { Ellipse, Wave, Diagonal, ZigZag, Custom };
    public enum ButtonPosition
    {
        TopLeft, TopCenter, TopRight, LeftCenter,
        RightCenter, BottomLeft, BottomCenter, BottomRight
    };


    /// <summary>
    /// The Carousel itself is based on PathListBox and PathListBoxUtils 
    /// using the codeplex available PathListBoxutils
    /// http://expressionblend.codeplex.com/wikipage?title=CarouselWPFTestAppUtils&referringTitle=Documentation
    /// 
    /// This article is also useful to see how the basic Carousel is made
    /// http://www.microsoft.com/design/toolbox/tutorials/CarouselWPFTestApp/carousel.aspx
    /// </summary>
    public partial class CarouselControl : UserControl
    {
        #region Data
        private List<PathListBoxItemTransformer> transformers = new List<PathListBoxItemTransformer>();
        private PathListBoxScrollBehavior pathListBoxScrollBehavior;
        #endregion

        #region Ctor
        public CarouselControl()
        {
            InitializeComponent();
            this.Loaded += CarouselControl_Loaded;
            pathListBox.SelectionChanged += PathListBox_SelectionChanged;
        }

        #endregion

        #region Private Methods

        private static bool IsValidNumberOfItemsOnPath(object value)
        {
            int v = (int)value;
            return (!v.Equals(int.MinValue) && !v.Equals(int.MaxValue));
        }


        private void SetButtonPosition(ButtonPosition buttonPosition)
        {
            switch (buttonPosition)
            {
                case ButtonPosition.TopLeft:
                    spButtons.VerticalAlignment = VerticalAlignment.Top;
                    spButtons.HorizontalAlignment = HorizontalAlignment.Left;
                    spButtons.Orientation = System.Windows.Controls.Orientation.Horizontal;
                    break;
                case ButtonPosition.TopCenter:
                    spButtons.VerticalAlignment = VerticalAlignment.Top;
                    spButtons.HorizontalAlignment = HorizontalAlignment.Center;
                    spButtons.Orientation = System.Windows.Controls.Orientation.Horizontal;
                    break;
                case ButtonPosition.TopRight:
                    spButtons.VerticalAlignment = VerticalAlignment.Top;
                    spButtons.HorizontalAlignment = HorizontalAlignment.Right;
                    spButtons.Orientation = System.Windows.Controls.Orientation.Horizontal;
                    break;
                case ButtonPosition.LeftCenter:
                    spButtons.VerticalAlignment = VerticalAlignment.Center;
                    spButtons.HorizontalAlignment = HorizontalAlignment.Left;
                    spButtons.Orientation = System.Windows.Controls.Orientation.Vertical;
                    break;
                case ButtonPosition.RightCenter:
                    spButtons.VerticalAlignment = VerticalAlignment.Center;
                    spButtons.HorizontalAlignment = HorizontalAlignment.Right;
                    spButtons.Orientation = System.Windows.Controls.Orientation.Vertical;
                    break;
                case ButtonPosition.BottomLeft:
                    spButtons.VerticalAlignment = VerticalAlignment.Bottom;
                    spButtons.HorizontalAlignment = HorizontalAlignment.Left;
                    spButtons.Orientation = System.Windows.Controls.Orientation.Horizontal;
                    break;
                case ButtonPosition.BottomCenter:
                    spButtons.VerticalAlignment = VerticalAlignment.Bottom;
                    spButtons.HorizontalAlignment = HorizontalAlignment.Center;
                    spButtons.Orientation = System.Windows.Controls.Orientation.Horizontal;
                    break;
                case ButtonPosition.BottomRight:
                    spButtons.VerticalAlignment = VerticalAlignment.Bottom;
                    spButtons.HorizontalAlignment = HorizontalAlignment.Right;
                    spButtons.Orientation = System.Windows.Controls.Orientation.Horizontal;
                    break;

            }
        }

        private void SetVisibilityForPath(PathType pathType)
        {
            foreach (UIElement uiElement in gridForKnownPaths.Children)
            {
                uiElement.Visibility = Visibility.Collapsed;
            }

            switch (pathType)
            {
                case PathType.Ellipse:
                    this.ellipsePath.Visibility = Visibility.Visible;
                    pathListBox.LayoutPaths[0].SourceElement = this.ellipsePath;
                    break;
                case PathType.Wave:
                    this.wavePath.Visibility = Visibility.Visible;
                    pathListBox.LayoutPaths[0].SourceElement = this.wavePath;
                    break;
                case PathType.Diagonal:
                    this.diagonalPath.Visibility = Visibility.Visible;
                    pathListBox.LayoutPaths[0].SourceElement = this.diagonalPath;
                    break;
                case PathType.ZigZag:
                    this.zigzagPath.Visibility = Visibility.Visible;
                    pathListBox.LayoutPaths[0].SourceElement = this.zigzagPath;
                    break;
                case PathType.Custom:
                    pathListBox.LayoutPaths[0].SourceElement = CustomPathElement;
                    break;

            }
        }

        private void CarouselControl_Loaded(object sender, RoutedEventArgs e)
        {
            BehaviorCollection behaviors = Interaction.GetBehaviors(pathListBox);
            pathListBoxScrollBehavior = (PathListBoxScrollBehavior)behaviors
                .Where(x => x.GetType() == typeof(PathListBoxScrollBehavior)).FirstOrDefault();
        }

        private void PathListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            RaiseSelectionChangedEvent(e);
        }

        private void PathListBoxItemTransformer_Loaded(object sender, RoutedEventArgs e)
        {
            PathListBoxItemTransformer trans = sender as PathListBoxItemTransformer;
            trans.Ease = AnimationEaseIn;
            trans.OpacityRange = OpacityRange;
            trans.ScaleRange = ScaleRange;
            trans.AngleRange = AngleRange;
            transformers.Add(trans);

        }


        /// <summary>
        /// The pathListBox transitioning did not seem to work when I used an ICommand
        /// could be it was firing to often and causing some mischief to happen
        /// </summary>
        private void PreviousButton_Click(object sender, RoutedEventArgs e)
        {
            if (pathListBox.SelectedIndex > 0)
            {
                pathListBox.SelectedIndex = pathListBox.SelectedIndex - 1;
            }
            else
            {
                pathListBox.SelectedIndex = pathListBox.Items.Count - 1;
            }

            pathListBox.SelectedItem = pathListBox.Items[pathListBox.SelectedIndex];
        }

        /// <summary>
        /// The pathListBox transitioning did not seem to work when I used an ICommand
        /// could be it was firing to often and causing some mischief to happen
        /// </summary>
        private void NextButton_Click(object sender, RoutedEventArgs e)
        {
            if (pathListBox.SelectedIndex < pathListBox.Items.Count - 1)
            {
                pathListBox.SelectedIndex = pathListBox.SelectedIndex + 1;
            }
            else
            {
                pathListBox.SelectedIndex = 0;
            }
            pathListBox.SelectedItem = pathListBox.Items[pathListBox.SelectedIndex];
        }

        #endregion

        #region Events

        #region SelectionChanged

        /// <summary>
        /// SelectionChanged Routed Event, which is raised when
        /// the internal PathListBox.SelectionChanged event occurs
        /// </summary>
        public static readonly RoutedEvent SelectionChangedEvent =
            EventManager.RegisterRoutedEvent("SelectionChanged",
            RoutingStrategy.Bubble, typeof(SelectionChangedEventHandler),
            typeof(CarouselControl));


        public event SelectionChangedEventHandler SelectionChanged
        {
            add { AddHandler(SelectionChangedEvent, value); }
            remove { RemoveHandler(SelectionChangedEvent, value); }
        }

        private SelectionChangedEventArgs RaiseSelectionChangedEvent(SelectionChangedEventArgs arg)
        {
            arg.RoutedEvent = SelectionChangedEvent;
            RoutedEventHelper.RaiseEvent(this, arg);
            return arg;
        }

        #endregion

        #endregion

        #region DPs

        #region CustomPathElement

        /// <summary>
        /// The FrameworkElement to use as custom path for Carousel
        /// </summary>
        public static readonly DependencyProperty CustomPathElementProperty =
            DependencyProperty.Register("CustomPathElement", typeof(FrameworkElement), typeof(CarouselControl),
                new FrameworkPropertyMetadata((FrameworkElement)null,
                    new PropertyChangedCallback(OnCustomPathElementChanged)));

        public FrameworkElement CustomPathElement
        {
            get { return (FrameworkElement)GetValue(CustomPathElementProperty); }
            set { SetValue(CustomPathElementProperty, value); }
        }

        private static void OnCustomPathElementChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((CarouselControl)d).OnCustomPathElementChanged(e);
        }

        protected virtual void OnCustomPathElementChanged(DependencyPropertyChangedEventArgs e)
        {
            if (e.NewValue != null)
                SetVisibilityForPath(PathType.Custom);
        }

        #endregion

        #region NavigationButtonPosition

        /// <summary>
        /// The Navigation ButtonPosition to use for the Carousel
        /// </summary>
        public static readonly DependencyProperty NavigationButtonPositionProperty =
            DependencyProperty.Register("NavigationButtonPosition", typeof(ButtonPosition), typeof(CarouselControl),
                new FrameworkPropertyMetadata((ButtonPosition)ButtonPosition.BottomCenter,
                    new PropertyChangedCallback(OnNavigationButtonPositionChanged)));

        public ButtonPosition NavigationButtonPosition
        {
            get { return (ButtonPosition)GetValue(NavigationButtonPositionProperty); }
            set { SetValue(NavigationButtonPositionProperty, value); }
        }

        private static void OnNavigationButtonPositionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((CarouselControl)d).OnNavigationButtonPositionChanged(e);
        }

        protected virtual void OnNavigationButtonPositionChanged(DependencyPropertyChangedEventArgs e)
        {
            if (e.NewValue != null)
                SetButtonPosition((ButtonPosition)e.NewValue);
        }

        #endregion

        #region PreviousButtonStyle

        /// <summary>
        /// Can be used to specify a new Style for Previous button to use for the Carousel
        /// </summary>
        public static readonly DependencyProperty PreviousButtonStyleProperty =
            DependencyProperty.Register("PreviousButtonStyle", typeof(Style), typeof(CarouselControl),
                new FrameworkPropertyMetadata((Style)null,
                    new PropertyChangedCallback(OnPreviousButtonStyleChanged)));

        public Style PreviousButtonStyle
        {
            get { return (Style)GetValue(PreviousButtonStyleProperty); }
            set { SetValue(PreviousButtonStyleProperty, value); }
        }

        private static void OnPreviousButtonStyleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((CarouselControl)d).OnPreviousButtonStyleChanged(e);
        }

        protected virtual void OnPreviousButtonStyleChanged(DependencyPropertyChangedEventArgs e)
        {
            if (e.NewValue != null)
                this.previousButton.Style = (Style)e.NewValue;
        }

        #endregion

        #region NextButtonStyle

        /// <summary>
        /// Can be used to specify a new Style for Next button to use for the Carousel
        /// </summary>
        public static readonly DependencyProperty NextButtonStyleProperty =
            DependencyProperty.Register("NextButtonStyle", typeof(Style), typeof(CarouselControl),
                new FrameworkPropertyMetadata((Style)null,
                    new PropertyChangedCallback(OnNextButtonStyleChanged)));

        public Style NextButtonStyle
        {
            get { return (Style)GetValue(NextButtonStyleProperty); }
            set { SetValue(NextButtonStyleProperty, value); }
        }

        private static void OnNextButtonStyleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((CarouselControl)d).OnNextButtonStyleChanged(e);
        }

        protected virtual void OnNextButtonStyleChanged(DependencyPropertyChangedEventArgs e)
        {
            if (e.NewValue != null)
                this.nextButton.Style = (Style)e.NewValue;
        }

        #endregion

        #region PathType

        /// <summary>
        /// PathType Dependency Property
        /// </summary>
        public static readonly DependencyProperty PathTypeProperty =
            DependencyProperty.Register("PathType", typeof(PathType), typeof(CarouselControl),
                new FrameworkPropertyMetadata((PathType)PathType.Custom,
                    new PropertyChangedCallback(OnPathTypeChanged)));

        public PathType PathType
        {
            get { return (PathType)GetValue(PathTypeProperty); }
            set { SetValue(PathTypeProperty, value); }
        }

        private static void OnPathTypeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((CarouselControl)d).OnPathTypeChanged(e);
        }

        protected virtual void OnPathTypeChanged(DependencyPropertyChangedEventArgs e)
        {
            SetVisibilityForPath((PathType)e.NewValue);
        }

        #endregion

        #region AnimationEaseIn

        /// <summary>
        /// AnimationEaseIn Dependency Property
        /// </summary>
        public static readonly DependencyProperty AnimationEaseInProperty =
            DependencyProperty.Register("AnimationEaseIn", typeof(EasingFunctionBase), typeof(CarouselControl),
                new FrameworkPropertyMetadata((EasingFunctionBase)null,
                    new PropertyChangedCallback(OnAnimationEaseInChanged)));

        public EasingFunctionBase AnimationEaseIn
        {
            get { return (EasingFunctionBase)GetValue(AnimationEaseInProperty); }
            set { SetValue(AnimationEaseInProperty, value); }
        }

        private static void OnAnimationEaseInChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((CarouselControl)d).OnAnimationEaseInChanged(e);
        }

        protected virtual void OnAnimationEaseInChanged(DependencyPropertyChangedEventArgs e)
        {
            if (e.NewValue != null)
            {
                foreach (PathListBoxItemTransformer pathListBoxItemTransformer in transformers)
                {
                    pathListBoxItemTransformer.Ease = (EasingFunctionBase)e.NewValue;
                }
            }
        }

        #endregion

        #region AnimationEaseOut

        /// <summary>
        /// AnimationEaseOut Dependency Property
        /// </summary>
        public static readonly DependencyProperty AnimationEaseOutProperty =
            DependencyProperty.Register("AnimationEaseOut", typeof(EasingFunctionBase), typeof(CarouselControl),
                new FrameworkPropertyMetadata((EasingFunctionBase)null,
                    new PropertyChangedCallback(OnAnimationEaseOutChanged)));

        public EasingFunctionBase AnimationEaseOut
        {
            get { return (EasingFunctionBase)GetValue(AnimationEaseOutProperty); }
            set { SetValue(AnimationEaseOutProperty, value); }
        }

        private static void OnAnimationEaseOutChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((CarouselControl)d).OnAnimationEaseOutChanged(e);
        }

        protected virtual void OnAnimationEaseOutChanged(DependencyPropertyChangedEventArgs e)
        {
            if (e.NewValue != null)
            {
                if (pathListBoxScrollBehavior != null)
                    pathListBoxScrollBehavior.Ease = (EasingFunctionBase)e.NewValue;
            }
        }

        #endregion

        #region OpacityRange

        /// <summary>
        /// OpacityRange to use for PathListBoxItemTransformer
        /// </summary>
        public static readonly DependencyProperty OpacityRangeProperty =
            DependencyProperty.Register("OpacityRange", typeof(Point), typeof(CarouselControl),
                new FrameworkPropertyMetadata((Point)new Point(0.7,1.0),
                    new PropertyChangedCallback(OnOpacityRangeChanged)));

        public Point OpacityRange
        {
            get { return (Point)GetValue(OpacityRangeProperty); }
            set { SetValue(OpacityRangeProperty, value); }
        }

        /// <summary>
        /// Handles changes to the OpacityRange property.
        /// </summary>
        private static void OnOpacityRangeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((CarouselControl)d).OnOpacityRangeChanged(e);
        }

        /// <summary>
        /// Provides derived classes an opportunity to handle changes to the OpacityRange property.
        /// </summary>
        protected virtual void OnOpacityRangeChanged(DependencyPropertyChangedEventArgs e)
        {
            foreach (PathListBoxItemTransformer pathListBoxItemTransformer in transformers)
            {
                pathListBoxItemTransformer.OpacityRange = (Point)e.NewValue;
            }

        }

        #endregion

        #region ScaleRange

        /// <summary>
        /// ScaleRange to use for PathListBoxItemTransformer
        /// </summary>
        public static readonly DependencyProperty ScaleRangeProperty =
            DependencyProperty.Register("ScaleRange", typeof(Point), typeof(CarouselControl),
                new FrameworkPropertyMetadata((Point)new Point(1.0,3.0),
                    new PropertyChangedCallback(OnScaleRangeChanged)));

        public Point ScaleRange
        {
            get { return (Point)GetValue(ScaleRangeProperty); }
            set { SetValue(ScaleRangeProperty, value); }
        }

        /// <summary>
        /// Handles changes to the ScaleRange property.
        /// </summary>
        private static void OnScaleRangeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((CarouselControl)d).OnScaleRangeChanged(e);
        }

        /// <summary>
        /// Provides derived classes an opportunity to handle changes to the ScaleRange property.
        /// </summary>
        protected virtual void OnScaleRangeChanged(DependencyPropertyChangedEventArgs e)
        {
            foreach (PathListBoxItemTransformer pathListBoxItemTransformer in transformers)
            {
                pathListBoxItemTransformer.ScaleRange = (Point)e.NewValue;
            }

        }

        #endregion

        #region AngleRange

        /// <summary>
        /// AngleRange to use for PathListBoxItemTransformer
        /// </summary>
        public static readonly DependencyProperty AngleRangeProperty =
            DependencyProperty.Register("AngleRange", typeof(Point), typeof(CarouselControl),
                new FrameworkPropertyMetadata((Point)new Point(0.0,0.0),
                    new PropertyChangedCallback(OnAngleRangeChanged)));

        public Point AngleRange
        {
            get { return (Point)GetValue(AngleRangeProperty); }
            set { SetValue(AngleRangeProperty, value); }
        }

        /// <summary>
        /// Handles changes to the AngleRange property.
        /// </summary>
        private static void OnAngleRangeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((CarouselControl)d).OnAngleRangeChanged(e);
        }

        /// <summary>
        /// Provides derived classes an opportunity to handle changes to the AngleRange property.
        /// </summary>
        protected virtual void OnAngleRangeChanged(DependencyPropertyChangedEventArgs e)
        {
            foreach (PathListBoxItemTransformer pathListBoxItemTransformer in transformers)
            {
                pathListBoxItemTransformer.AngleRange = (Point)e.NewValue;
            }

        }

        #endregion

 
         //Path ListBox wrapped properties


        #region DataTemplateToUse
        /// <summary>
        /// The DataTemplate to use for the Carousel
        /// </summary>
        [Bindable(true)]
        public static readonly DependencyProperty DataTemplateToUseProperty =
            DependencyProperty.Register("DataTemplateToUse", typeof(DataTemplate), typeof(CarouselControl),
                new FrameworkPropertyMetadata((DataTemplate)null,
                    new PropertyChangedCallback(OnDataTemplateToUseChanged)));

        public DataTemplate DataTemplateToUse
        {
            get { return (DataTemplate)GetValue(DataTemplateToUseProperty); }
            set { SetValue(DataTemplateToUseProperty, value); }
        }

        private static void OnDataTemplateToUseChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((CarouselControl)d).OnDataTemplateToUseChanged(e);
        }

        protected virtual void OnDataTemplateToUseChanged(DependencyPropertyChangedEventArgs e)
        {
            if (e.NewValue != null)
                this.pathListBox.ItemTemplate = (DataTemplate)e.NewValue;
        }
        #endregion

        #region SelectedItem

        /// <summary>
        /// SelectedItem Dependency Property
        /// </summary>
        public static readonly DependencyProperty SelectedItemProperty =
            DependencyProperty.Register("SelectedItem", typeof(Object), typeof(CarouselControl),
                new FrameworkPropertyMetadata((Object)null,
                    new PropertyChangedCallback(OnSelectedItemChanged)));

        public Object SelectedItem
        {
            get { return (Object)pathListBox.GetValue(SelectedItemProperty); }
            set { pathListBox.SetValue(SelectedItemProperty, value); }
        }

        private static void OnSelectedItemChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((CarouselControl)d).OnSelectedItemChanged(e);
        }

        protected virtual void OnSelectedItemChanged(DependencyPropertyChangedEventArgs e)
        {
            object currentlySelected = pathListBox.SelectedItem;
            if (currentlySelected != e.NewValue)
            {
                pathListBox.SelectedItem = e.NewValue;
            }
        }



        #endregion

        #region ItemsSource

        /// <summary>
        /// The ItemsSource to use for the Carousel
        /// </summary>
        [Bindable(true)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public static readonly DependencyProperty ItemsSourceProperty =
            DependencyProperty.Register("ItemsSource", typeof(IEnumerable), typeof(CarouselControl),
                new FrameworkPropertyMetadata((IEnumerable)null,
                    new PropertyChangedCallback(OnItemsSourceChanged)));

        public IEnumerable ItemsSource
        {
            get { return (IEnumerable)GetValue(ItemsSourceProperty); }
            set { SetValue(ItemsSourceProperty, value); }
        }

        private static void OnItemsSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((CarouselControl)d).OnItemsSourceChanged(e);
        }

        protected virtual void OnItemsSourceChanged(DependencyPropertyChangedEventArgs e)
        {
            if (e.NewValue != null)
            {
                transformers.Clear();
                this.pathListBox.ItemsSource = (IEnumerable)e.NewValue;
            }
        }

        #endregion

        #region NumberOfItemsOnPath



        /// <summary>
        /// NumberOfItemsOnPath
        /// </summary>
        public static readonly DependencyProperty NumberOfItemsOnPathProperty = DependencyProperty.Register(
            "NumberOfItemsOnPath",
            typeof(int),
            typeof(CarouselControl),
            new FrameworkPropertyMetadata(
                7,
                FrameworkPropertyMetadataOptions.None,
                new PropertyChangedCallback(OnNumberOfItemsOnPathChanged),
                new CoerceValueCallback(CoerceNumberOfItemsOnPath)
            ),
            new ValidateValueCallback(IsValidNumberOfItemsOnPath)
        );

        //property accessors
        public int NumberOfItemsOnPath
        {
            get { return (int)GetValue(NumberOfItemsOnPathProperty); }
            set { SetValue(NumberOfItemsOnPathProperty, value); }
        }


        /// <summary>
        /// Coerce NumberOfItemsOnPath value if not within limits
        /// </summary>
        private static object CoerceNumberOfItemsOnPath(DependencyObject d, object value)
        {
            CarouselControl depObj = (CarouselControl)d;
            int current = (int)value;
            if (current < depObj.MinNumberOfItemsOnPath) current = depObj.MinNumberOfItemsOnPath;
            if (current > depObj.MaxNumberOfItemsOnPath) current = depObj.MaxNumberOfItemsOnPath;
            return current;
        }

        private static void OnNumberOfItemsOnPathChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            d.CoerceValue(MinNumberOfItemsOnPathProperty);  //invokes the CoerceValueCallback delegate ("CoerceMinNumberOfItemsOnPath")
            d.CoerceValue(MaxNumberOfItemsOnPathProperty);  //invokes the CoerceValueCallback delegate ("CoerceMaxNumberOfItemsOnPath")
            CarouselControl depObj = (CarouselControl)d;
            depObj.pathListBox.LayoutPaths[0].Capacity = (double)depObj.NumberOfItemsOnPath;


        }
        #endregion

        #region MinNumberOfItemsOnPath

        /// <summary>
        /// MinNumberOfItemsOnPath DP
        /// </summary>
        public static readonly DependencyProperty MinNumberOfItemsOnPathProperty = DependencyProperty.Register(
        "MinNumberOfItemsOnPath",
        typeof(int),
        typeof(CarouselControl),
        new FrameworkPropertyMetadata(
            3,
            FrameworkPropertyMetadataOptions.None,
            new PropertyChangedCallback(OnMinNumberOfItemsOnPathChanged),
            new CoerceValueCallback(CoerceMinNumberOfItemsOnPath)
        ),
        new ValidateValueCallback(IsValidNumberOfItemsOnPath));

        //property accessors
        public int MinNumberOfItemsOnPath
        {
            get { return (int)GetValue(MinNumberOfItemsOnPathProperty); }
            set { SetValue(MinNumberOfItemsOnPathProperty, value); }
        }

        /// <summary>
        /// Coerce MinNumberOfItemsOnPath value if not within limits
        /// </summary>
        private static void OnMinNumberOfItemsOnPathChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            d.CoerceValue(MaxNumberOfItemsOnPathProperty);  //invokes the CoerceValueCallback delegate ("CoerceMaxNumberOfItemsOnPath")
            d.CoerceValue(NumberOfItemsOnPathProperty);  //invokes the CoerceValueCallback delegate ("CoerceNumberOfItemsOnPath")
        }

        private static object CoerceMinNumberOfItemsOnPath(DependencyObject d, object value)
        {
            CarouselControl depObj = (CarouselControl)d;
            int min = (int)value;
            if (min > depObj.MaxNumberOfItemsOnPath) min = depObj.MaxNumberOfItemsOnPath;
            return min;
        }
        #endregion

        #region MaxNumberOfItemsOnPath

        /// <summary>
        /// MaxNumberOfItemsOnPath
        /// </summary>
        public static readonly DependencyProperty MaxNumberOfItemsOnPathProperty = DependencyProperty.Register(
            "MaxNumberOfItemsOnPath",
            typeof(int),
            typeof(CarouselControl),
            new FrameworkPropertyMetadata(
                10,
                FrameworkPropertyMetadataOptions.None,
                new PropertyChangedCallback(OnMaxNumberOfItemsOnPathChanged),
                new CoerceValueCallback(CoerceMaxNumberOfItemsOnPath)
            ),
            new ValidateValueCallback(IsValidNumberOfItemsOnPath)
        );

        //property accessors
        public int MaxNumberOfItemsOnPath
        {
            get { return (int)GetValue(MaxNumberOfItemsOnPathProperty); }
            set { SetValue(MaxNumberOfItemsOnPathProperty, value); }
        }

        /// <summary>
        /// Coerce MaxNumberOfItemsOnPath value if not within limits
        /// </summary>
        private static object CoerceMaxNumberOfItemsOnPath(DependencyObject d, object value)
        {
            CarouselControl depObj = (CarouselControl)d;
            int max = (int)value;
            if (max < depObj.MinNumberOfItemsOnPath) max = depObj.MinNumberOfItemsOnPath;
            return max;
        }

        private static void OnMaxNumberOfItemsOnPathChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            d.CoerceValue(MinNumberOfItemsOnPathProperty);  //invokes the CoerceValueCallback delegate ("CoerceMinNumberOfItemsOnPath")
            d.CoerceValue(NumberOfItemsOnPathProperty);  //invokes the CoerceValueCallback delegate ("CoerceNumberOfItemsOnPath")
        }
        #endregion

        #endregion
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United Kingdom United Kingdom
I currently hold the following qualifications (amongst others, I also studied Music Technology and Electronics, for my sins)

- MSc (Passed with distinctions), in Information Technology for E-Commerce
- BSc Hons (1st class) in Computer Science & Artificial Intelligence

Both of these at Sussex University UK.

Award(s)

I am lucky enough to have won a few awards for Zany Crazy code articles over the years

  • Microsoft C# MVP 2016
  • Codeproject MVP 2016
  • Microsoft C# MVP 2015
  • Codeproject MVP 2015
  • Microsoft C# MVP 2014
  • Codeproject MVP 2014
  • Microsoft C# MVP 2013
  • Codeproject MVP 2013
  • Microsoft C# MVP 2012
  • Codeproject MVP 2012
  • Microsoft C# MVP 2011
  • Codeproject MVP 2011
  • Microsoft C# MVP 2010
  • Codeproject MVP 2010
  • Microsoft C# MVP 2009
  • Codeproject MVP 2009
  • Microsoft C# MVP 2008
  • Codeproject MVP 2008
  • And numerous codeproject awards which you can see over at my blog

Written By
Instructor / Trainer Alura Cursos Online
Brazil Brazil

Comments and Discussions