using System; using System.Collections.Generic; using System.Diagnostics; 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.Animation; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; namespace Tango.SharedUI.Controls { /// /// Represents a control for holding a list of content controls and creates transition effects between them. /// public partial class MultiTransitionControl : UserControl { private int index; private bool freezeAnimation; private ContentControl element1 = null; private ContentControl element2 = null; private ContentControl fromElement; private ContentControl toElement; private Action navigationCompleteAction; private bool _loaded; #region Events /// /// Occurs when the transition has completed. /// public event Action TransitionComplete; #endregion #region Constructors /// /// Initialize a new instance of MultiTransitionControl. /// public MultiTransitionControl() { Controls = new List(); index = 0; InitializeComponent(); this.Loaded += MultiTransitionControl_Loaded; } #endregion #region Event Handlers private void MultiTransitionControl_Loaded(object sender, RoutedEventArgs e) { if (!_loaded) { RefreshControls(); _loaded = true; } } public void RefreshControls() { mainGrid.Children.Clear(); foreach (ContentControl control in Controls) { control.Visibility = System.Windows.Visibility.Hidden; mainGrid.Children.Add(control); } if (Controls.Count > 0) { Controls[0].Visibility = System.Windows.Visibility.Visible; SelectedControl = Controls[0]; fromElement = SelectedControl; } } private void ani_BackCompleted(object sender, EventArgs e) { fromElement = toElement; toElement = null; freezeAnimation = true; this.ApplyAnimationClock(MultiTransitionControl.MixProperty, null); Mix = 0; index++; freezeAnimation = false; IsAnimating = false; OnTransitionCompleted(); } private void ani_NextCompleted(object sender, EventArgs e) { fromElement = toElement; toElement = null; freezeAnimation = true; this.ApplyAnimationClock(MultiTransitionControl.MixProperty, null); Mix = 0; index++; freezeAnimation = false; IsAnimating = false; OnTransitionCompleted(); } #endregion #region Properties /// /// Gets or sets the available content controls nested in the MultiTransitionControl. /// public List Controls { get; set; } /// /// Gets or sets the transition style. /// public TransitionTypeEnum TransitionType { get { return (TransitionTypeEnum)GetValue(TransitionTypeProperty); } set { SetValue(TransitionTypeProperty, value); } } public static readonly DependencyProperty TransitionTypeProperty = DependencyProperty.Register("TransitionType", typeof(TransitionTypeEnum), typeof(MultiTransitionControl), new PropertyMetadata(TransitionTypeEnum.Fade) { PropertyChangedCallback = new PropertyChangedCallback(TransitionTypeChanged) }); private static void TransitionTypeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { (d as MultiTransitionControl).ResetTransformation(); } /// /// This is where the actual transition occurs. /// private double Mix { get { return (double)GetValue(MixProperty); } set { SetValue(MixProperty, value); if (!freezeAnimation) { UpdateMix(value); } } } private static readonly DependencyProperty MixProperty = DependencyProperty.Register("Mix", typeof(double), typeof(MultiTransitionControl), new PropertyMetadata(new PropertyChangedCallback(MixChanged))); private static void MixChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { MultiTransitionControl tr = d as MultiTransitionControl; double value = (double)e.NewValue; tr.UpdateMix(value); } /// /// Gets or sets whether to always include a cross fade transition along with the selected transition style. /// public bool AlwaysFade { get { return (bool)GetValue(AlwaysFadeProperty); } set { SetValue(AlwaysFadeProperty, value); } } public static readonly DependencyProperty AlwaysFadeProperty = DependencyProperty.Register("AlwaysFade", typeof(bool), typeof(MultiTransitionControl), new PropertyMetadata(false)); /// /// Gets or sets the interval in milliseconds for the transition to complete. /// public int AnimationTime { get { return (int)GetValue(AnimationTimeProperty); } set { SetValue(AnimationTimeProperty, value); } } public static readonly DependencyProperty AnimationTimeProperty = DependencyProperty.Register("AnimationTime", typeof(int), typeof(MultiTransitionControl), new PropertyMetadata(1000)); /// /// Gets or sets the transition 'ease in' time. /// public double AnimationAcceleration { get { return (double)GetValue(AnimationAccelerationProperty); } set { SetValue(AnimationAccelerationProperty, value); } } public static readonly DependencyProperty AnimationAccelerationProperty = DependencyProperty.Register("AnimationAcceleration", typeof(double), typeof(MultiTransitionControl), new PropertyMetadata(0.5)); /// /// Gets or sets the transition 'ease out' time. /// public double AnimationDeceleration { get { return (double)GetValue(AnimationDecelerationProperty); } set { SetValue(AnimationDecelerationProperty, value); } } public static readonly DependencyProperty AnimationDecelerationProperty = DependencyProperty.Register("AnimationDeceleration", typeof(double), typeof(MultiTransitionControl), new PropertyMetadata(0.5)); /// /// Gets or sets whether a transition is taking place at the moment. /// public bool IsAnimating { get { return (bool)GetValue(IsAnimatingProperty); } private set { SetValue(IsAnimatingProperty, value); } } public static readonly DependencyProperty IsAnimatingProperty = DependencyProperty.Register("IsAnimating", typeof(bool), typeof(MultiTransitionControl), new PropertyMetadata(false)); /// /// Gets or sets currently visible content control. /// public ContentControl SelectedControl { get { return (ContentControl)GetValue(SelectedControlProperty); } set { SetValue(SelectedControlProperty, value); } } public static readonly DependencyProperty SelectedControlProperty = DependencyProperty.Register("SelectedControl", typeof(ContentControl), typeof(MultiTransitionControl), new PropertyMetadata(null)); #endregion #region Methods /// /// Updates the mix. /// /// The value. private void UpdateMix(double value) { if (freezeAnimation) return; if (element1 == null || element2 == null) { return; } element1.Visibility = System.Windows.Visibility.Visible; element2.Visibility = System.Windows.Visibility.Visible; if (TransitionType == TransitionTypeEnum.Fade) { element1.RenderTransform = null; element2.RenderTransform = null; element1.Opacity = value; element2.Opacity = 1 - value; return; } else if (TransitionType == TransitionTypeEnum.Slide) { double v = value * 100; v = (v * this.ActualWidth) / 100; element2.RenderTransform = new TranslateTransform(v, 0); element1.RenderTransform = new TranslateTransform(v - this.ActualWidth, 0); } else if (TransitionType == TransitionTypeEnum.Zoom) { element1.RenderTransformOrigin = new Point(0.5, 0.5); element2.RenderTransformOrigin = new Point(0.5, 0.5); element1.RenderTransform = new ScaleTransform(value, value); element2.RenderTransform = new ScaleTransform(1 - value, 1 - value); } else if (TransitionType == TransitionTypeEnum.Spin) { double v = value * 100; v = (v * 360) / 100; var group1 = new TransformGroup(); group1.Children.Add(new RotateTransform(v)); group1.Children.Add(new ScaleTransform(value, value)); var group2 = new TransformGroup(); group2.Children.Add(new RotateTransform(360 - v)); group2.Children.Add(new ScaleTransform(1 - value, 1 - value)); element1.RenderTransform = group1; element2.RenderTransform = group2; } else if (TransitionType == TransitionTypeEnum.Carousel) { double v = value * 100; v = (v * this.ActualWidth) / 100; var group1 = new TransformGroup(); group1.Children.Add(new ScaleTransform(value, value)); group1.Children.Add(new TranslateTransform(v - this.ActualWidth, 0)); var group2 = new TransformGroup(); group2.Children.Add(new TranslateTransform(v * 2, 0)); group2.Children.Add(new ScaleTransform(1 - value, 1 - value)); element1.RenderTransform = group1; element2.RenderTransform = group2; } else if (TransitionType == TransitionTypeEnum.Skew) { double v = value * 100; v = (v * 180) / 100; element2.RenderTransform = new SkewTransform(v, 0); element1.RenderTransform = new SkewTransform(v - 180, 0); } else if (TransitionType == TransitionTypeEnum.Push) { element1.RenderTransformOrigin = new Point(1, 0.5); element2.RenderTransformOrigin = new Point(0, 0.5); element1.RenderTransform = new ScaleTransform(value, 1); element2.RenderTransform = new ScaleTransform(1 - value, 1); } if (AlwaysFade) { element1.Opacity = value; element2.Opacity = 1 - value; } } /// /// Resets the transformation. /// private void ResetTransformation() { foreach (ContentControl control in Controls) { control.RenderTransformOrigin = new Point(0.5, 0.5); } } /// /// Execute a transition between the current selected control and the specified content control tag. /// /// Tag of content control to navigate to. /// DataContext to assign to the specified content control. /// Navigation completed callback delegate. public void AutoNavigate(object tag, object dataContext = null, Action navigationComplete = null) { if (IsAnimating) { return; } ContentControl control = Controls.SingleOrDefault(x => x.Tag != null && x.Tag.ToString() == tag.ToString()); if (control == SelectedControl) { return; } if (control == null) { throw new Exception("Control with tag '" + tag.ToString() + "' not found! (Transition Control)"); } if (dataContext != null) { control.DataContext = dataContext; } if (Controls.IndexOf(control) > Controls.IndexOf(fromElement)) { navigationCompleteAction = navigationComplete; toElement = control; SelectedControl = control; Next(); } else if (Controls.IndexOf(control) < Controls.IndexOf(fromElement)) { navigationCompleteAction = navigationComplete; toElement = control; SelectedControl = control; Back(); } } private void Next() { if (IsAnimating) { return; } IsAnimating = true; if (Controls.IndexOf(toElement) > Controls.IndexOf(fromElement)) { element1 = toElement; element2 = fromElement; } else { element1 = fromElement; element2 = toElement; } if (element2 == null) { SelectedControl = Controls[0]; fromElement = SelectedControl; element2 = fromElement; } freezeAnimation = true; this.ApplyAnimationClock(MultiTransitionControl.MixProperty, null); Mix = 0; freezeAnimation = false; DoubleAnimation ani = new DoubleAnimation(1, new Duration(TimeSpan.FromMilliseconds(AnimationTime)), FillBehavior.HoldEnd); ani.AccelerationRatio = AnimationAcceleration; ani.DecelerationRatio = AnimationDeceleration; ani.Completed += ani_NextCompleted; this.BeginAnimation(MultiTransitionControl.MixProperty, ani); } private void Back() { if (IsAnimating) return; IsAnimating = true; if (Controls.IndexOf(toElement) > Controls.IndexOf(fromElement)) { element1 = toElement; element2 = fromElement; } else { element1 = fromElement; element2 = toElement; } freezeAnimation = true; this.ApplyAnimationClock(MultiTransitionControl.MixProperty, null); Mix = 1; freezeAnimation = false; DoubleAnimation ani = new DoubleAnimation(0, new Duration(TimeSpan.FromMilliseconds(AnimationTime)), FillBehavior.HoldEnd); ani.Completed += ani_BackCompleted; ani.AccelerationRatio = AnimationAcceleration; ani.DecelerationRatio = AnimationDeceleration; this.BeginAnimation(MultiTransitionControl.MixProperty, ani); } #endregion #region Virtuals /// /// Called when [transition completed]. /// protected virtual void OnTransitionCompleted() { if (TransitionComplete != null) TransitionComplete(this); if (SelectedControl != null) { Panel.SetZIndex(SelectedControl, Controls.Max(x => Panel.GetZIndex(x)) + 1); } if (navigationCompleteAction != null) { navigationCompleteAction(); navigationCompleteAction = null; } if (SelectedControl != null) { if (SelectedControl.Content is ITransitionView) { (SelectedControl.Content as ITransitionView).OnTransitionCompleted(); } } } public interface ITransitionView { void OnTransitionCompleted(); } #endregion } }