Click here to Skip to main content
15,893,668 members
Articles / Desktop Programming / Windows Forms

Simple Carousel Control for Windows Forms

Rate me:
Please Sign up or sign in to vote.
3.33/5 (7 votes)
24 Apr 2012CPOL2 min read 59.4K   8K   31  
Simple Carousel Control for Windows Forms, can host any content with in views.
using System;
using System.Text;
using System.Windows.Forms;
using System.Drawing;
using System.ComponentModel;
using System.Collections;
using Creatives.Windows.Metrics;

namespace Creatives.Windows.Controls
{
	public class Carousel : Control
	{
		#region Ctor

		public Carousel()
		{
			Size = new Size(200, 100);
			MinimumSize = new Size(100, 100);
			BackColor = Color.BurlyWood;
			ViewOffset = 20;
			ViewMargin = 20;

			SetStyle(ControlStyles.UserPaint, true);
			SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
			SetStyle(ControlStyles.AllPaintingInWmPaint, true);
		}

		#endregion

		#region Members

		int animating = 0;

		public bool IsAnimating
		{
			get
			{
				return animating != 0;
			}
		}

		/// <summary>
		/// Gets or sets the spacing between adjacent views.
		/// </summary>
		/// <remarks>Offset is not guranteed, valid only when engough space is available for layout. </remarks>
		public int ViewOffset
		{
			get;
			set;
		}

		private int viewPortItemsCount = 0;
		
		/// <summary>
		/// Gets or sets the number of Views visible within view port.
		/// </summary>
		public int ViewPortItemsCount
		{
			get 
			{
				return (viewPortItemsCount > Views.Count) 
					? Views.Count : 
					viewPortItemsCount;
			}
			set 
			{
				if (value > 0)
					viewPortItemsCount = value;
				else
					viewPortItemsCount = 0;
			}
		}

		private ViewCollection views = null;

		/// <summary>
		/// Gets or sets the views associated with the Carousel.
		/// </summary>
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
		public ViewCollection Views
		{
			get 
			{
				if (views == null)
				{
					views = new ViewCollection(this);
					views.CollectionChanged += new EventHandler(OnViewCollectionChanged);
				}

				return views;
			}
			set
			{
				if (views != null)
					views.CollectionChanged -= new EventHandler(OnViewCollectionChanged);

				views = value;

				if (views != null)
					views.CollectionChanged -= new EventHandler(OnViewCollectionChanged);

				PerformLayout();
			}
		}

		internal CircularList CircularViews { get; set; }

		private LayoutManager layoutManager = null;

		public View ActiveView
		{
			get;
			set;
		}

		/// <summary>
		/// Gets or sets the offset between carousel and the views.
		/// </summary>
		public int ViewMargin { get; set; }

		#endregion

		#region Overriden Members

		protected override void OnHandleCreated(EventArgs e)
		{
			base.OnHandleCreated(e);

			layoutManager = new LayoutManager(this);

			int visibleItems = (viewPortItemsCount == 0) ? Views.Count : viewPortItemsCount;

			if (visibleItems <= 0) return;

			double index = Math.Ceiling(((double)visibleItems / 2));
			
			ActiveView = Views[(int)index - 1];

			PerformLayout();
		}

		protected override void OnHandleDestroyed(EventArgs e)
		{
			layoutManager.Dispose();

			base.OnHandleDestroyed(e);
		}

		protected override void Dispose(bool disposing)
		{
			base.Dispose(disposing);

			if (disposing)
			{
				foreach (View view in Views)
					view.Dispose();

				Views.Clear();
			}
		}

		#endregion

		#region Implementations

		private void BeginAnimation()
		{
			animating++;
		}

		private void EndAnimation()
		{
			if (animating > 0)
				animating--;
		}

		private void OnViewCollectionChanged(object sender, EventArgs e)
		{
			UnWireViews();

			if (!(Disposing || IsDisposed))
			{
				WireViews();
				UpdateQueue();
				PerformLayout();
			}
		}

		private void UpdateQueue()
		{
			CircularViews = new CircularList(Views);
		}

		private void WireViews()
		{
			foreach(View view in this.Views)
				view.Activated += new EventHandler(OnViewActivated);
		}

		void OnViewActivated(object sender, EventArgs e)
		{
			View activeNew = (View)sender;

			if (ActiveView == activeNew) return;

			int indexActive = Array.IndexOf(CircularViews.ToArray(), ActiveView);
			int indexNew = Array.IndexOf(CircularViews.ToArray(), activeNew);
			
			ActiveView = activeNew;

			ShiftViews(indexActive, indexNew);
		}

		private void ShiftViews(int indexActive, int indexNew)
		{
			layoutManager.ShiftViews(Math.Abs(indexNew - indexActive), indexNew > indexActive);
		}

		private void UnWireViews()
		{
			foreach (View view in this.Views)
				view.Activated -= new EventHandler(OnViewActivated);
		}

		protected override void OnLayout(LayoutEventArgs levent)
		{
			base.OnLayout(levent);

			if (layoutManager != null && !IsAnimating)
				layoutManager.PerformLayout();
		}

		public void Activate(View view)
		{
 
		}

		public void ActivateNext()
		{
 
		}

		public void ActivatePrevious()
		{
 
		}

		#endregion

		#region ** Layout Manager

		private class LayoutManager
		{
			private Carousel _carousel;

			public delegate void InvokeDelegate();

			#region Ctor

			public LayoutManager(Carousel carousel)
			{
				_carousel = carousel;
			}

			#endregion

			#region Dispose
			
			/// <summary>
			/// Releases any references to the carousel control.
			/// </summary>
			public void Dispose()
			{
				this._carousel = null;
			}

			#endregion

			#region Implementation

			public void PerformLayout()
			{
				int heightAvailable = _carousel.Height -  2 *_carousel.ViewMargin ;
				int widthAvailable = _carousel.Width - 2 * _carousel.ViewMargin;
				int visibleItems = (_carousel.viewPortItemsCount == 0) ? _carousel.Views.Count : _carousel.viewPortItemsCount;
				int x = _carousel.ViewMargin;
				int y = _carousel.ViewMargin;

				if (visibleItems <= 0) return;

				int offsetX = _carousel.ViewOffset;
				int offsetY = 20;

				widthAvailable -= (visibleItems - 1) * offsetX;

				int itemWidth = widthAvailable / visibleItems;
				int itemHeight = heightAvailable;

				foreach (View view in _carousel.CircularViews)
				{
					itemHeight = (view == _carousel.ActiveView) ? heightAvailable : heightAvailable - offsetY;
				   
					view.Size = new Size(itemWidth, itemHeight);

					view.Location = new Point(x, y + ((heightAvailable - itemHeight) / 2));

					x += view.Width + offsetX;
				}
			}

			public void ShiftViews(int byNos, bool moveLeft)
			{
				int animOffset = byNos * (_carousel.ActiveView.Width + _carousel.ViewOffset + 2 * _carousel.ViewMargin);

				foreach (View view in _carousel.Views)
				{
					IntAnimation Xanimation = new IntAnimation
					{
						By = 1,
						Interval = 1,
						Duration = animOffset * 10,
						From = view.Left,
						To = (moveLeft) ? view.Left - animOffset : view.Left + animOffset,
					};

					Xanimation.Completed += new AnimationEventHandler<int>(Xanimation_Completed);

					_carousel.BeginAnimation();

					view.Animate("Left", Xanimation);
				}

				while (byNos > 0)
				{
					if (moveLeft)
						_carousel.CircularViews.ShiftLeft();
					else
						_carousel.CircularViews.ShiftRight();

					byNos--;
				}
			}

			private void ShiftLeft()
			{
				
			}

			void Xanimation_Completed(object sender, AnimationEventArgs<int> e)
			{
				e.Animation.Completed -= new AnimationEventHandler<int>(Xanimation_Completed);
				
				_carousel.EndAnimation();

				if (!_carousel.IsAnimating)
				{
					_carousel.Invoke(new InvokeDelegate(PerformLayout));
				}

			}

			private void ShiftRight()
			{
				int animOffset = _carousel.ActiveView.Width + _carousel.ViewOffset + 2 * _carousel.ViewMargin;

				foreach (View view in _carousel.Views)
				{
					IntAnimation Xanimation = new IntAnimation
					{
						By = 1,
						Interval = 1,
						Duration = animOffset * 10,
						From = view.Left,
						To = view.Left + animOffset
					};

					Xanimation.Completed += new AnimationEventHandler<int>(Xanimation_Completed);

					_carousel.BeginAnimation();

					view.Animate("Left", Xanimation);
				}

				_carousel.CircularViews.ShiftRight();
			}

			#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
Technical Lead
India India
I code, learn, read and listen.

Comments and Discussions