Click here to Skip to main content
15,891,757 members
Please Sign up or sign in to vote.
4.00/5 (1 vote)
Hi
I am using the following control for animated transitions between views:

C#
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Animation;
using EasyTouch.Gui.Selector;
using Microsoft.Expression.Media.Effects;

namespace EasyTouch.Gui.Control
{
    /// <summary>
    /// Control used to animate the transition between shown elements
    /// </summary>
    public class TransitionControl : ContentControl
    {
        // Private Fields
        private ContentPresenter _contentPresenter;

        /// <summary>
        /// Static constructor
        /// </summary>
        static TransitionControl()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(TransitionControl), new FrameworkPropertyMetadata(typeof(TransitionControl)));
            ContentProperty.OverrideMetadata(typeof(TransitionControl), new FrameworkPropertyMetadata(OnContentPropertyChanged));
        }

        /// <summary>
        /// ContentTransitionSelector depencency property
        /// </summary>
        public static readonly DependencyProperty ContentTransitionSelectorProperty = DependencyProperty.Register("ContentTransitionSelector", typeof (TransitionSelector), typeof (TransitionControl),
                                                                                                                  new UIPropertyMetadata(null));
        /// <summary>
        /// Gets or sets the TransitionSelector used to retrive the transition effect
        /// </summary>
        public TransitionSelector ContentTransitionSelector
        {
            get { return (TransitionSelector)GetValue(ContentTransitionSelectorProperty); }
            set { SetValue(ContentTransitionSelectorProperty, value); }
        }

        /// <summary>
        /// Transition duration dependency property
        /// </summary>
        public static readonly DependencyProperty DurationProperty = DependencyProperty.Register("Duration", typeof (TimeSpan), typeof (TransitionControl),
                                                                                                 new UIPropertyMetadata(TimeSpan.FromSeconds(1)));
        /// <summary>
        /// Gets or sets the transition duration
        /// </summary>
        public TimeSpan Duration
        {
            get { return (TimeSpan)GetValue(DurationProperty); }
            set { SetValue(DurationProperty, value); }
        }

        /// <summary>
        /// The EasingFunction depencendy property
        /// </summary>
        public static readonly DependencyProperty EasingFunctionProperty = DependencyProperty.Register("EasingFunction", typeof (IEasingFunction), typeof (TransitionControl),
                                                                                                        new UIPropertyMetadata(null));
        /// <summary>
        /// Gets or sets <see cref="IEasingFunction"/> instance used by the transition animation
        /// </summary>
        public IEasingFunction EasingFunction
        {
            get { return (IEasingFunction)GetValue(EasingFunctionProperty); }
            set { SetValue(EasingFunctionProperty, value); }
        }

        /// <summary>
        /// Enable transition dependency property
        /// </summary>
        public static readonly DependencyProperty EnableTransitionsProperty = DependencyProperty.Register("EnableTransitions", typeof(bool), typeof(TransitionControl),
                                                                                                            new UIPropertyMetadata(true));
        /// <summary>
        /// Gets or sets the value indicating whether the effect will be used during the content transition
        /// </summary>
        public bool EnableTransitions
        {
            get { return (bool)GetValue(EnableTransitionsProperty); }
            set { SetValue(EnableTransitionsProperty, value); }
        }

        /// <summary>
        /// Executes when the ContentProperty is changed
        /// </summary>
        /// <param name="dp">The control whose content has changed</param>
        /// <param name="args">Additional information about old and new content</param>
        private static void OnContentPropertyChanged(DependencyObject dp, DependencyPropertyChangedEventArgs args)
        {
            ((TransitionControl) dp).OnContentChanged(args.OldValue, args.NewValue);
        }

        /// <summary>
        /// Executes when the Content of the control is changed
        /// </summary>
        /// <param name="oldContent">The old content</param>
        /// <param name="newContent">The new content</param>
        protected override void OnContentChanged(object oldContent, object newContent)
        {
            if (oldContent != null && newContent != null)
            {
                AnimateContent(oldContent, newContent);
            }
            else
            {
                AnimateContent(newContent);
            }
        }

        /// <summary>
        /// Shows the new content using the animation
        /// </summary>
        /// <param name="content">The controls new content</param>
        private void AnimateContent(object content)
        {
            if(IsLoaded)
            {
                _contentPresenter.Content = content;
            }
            else
            {
                RoutedEventHandler handler = null;
                handler = delegate
                              {
                                  Loaded -= handler;
                                  _contentPresenter.Content = content;
                              };
                Loaded += handler;
            }
        }

        /// <summary>
        /// Shows the new content using the animation
        /// </summary>
        /// <param name="oldContent">The controls old content</param>
        /// <param name="newContent">The controls new content</param>
        private void AnimateContent(object oldContent, object newContent)
        {
            var oldContentVisual = GetVisualChild();
            var tier = (RenderCapability.Tier >> 16);

            // If the selector is not specified or the animation is not enabled or not supported show the new content without animation
            if (EnableTransitions == false || ContentTransitionSelector == null || oldContentVisual == null || tier < 2)
            {
                SetNonVisualChild(newContent);
                return;
            }

            // create the transition
            var transitionEffect = ContentTransitionSelector.GetTransition(oldContent, newContent, this);
            if (transitionEffect == null)
            {
                throw new InvalidOperationException("Returned transition effect is null.");
            }

            // create the animation
            var da = new DoubleAnimation(0.0, 1.0, new Duration(Duration), FillBehavior.HoldEnd);
            da.Completed += delegate
            {
                ApplyEffect(null);
            };
            if (EasingFunction != null)
            {
                da.EasingFunction = EasingFunction;
            }
            else
            {
                da.AccelerationRatio = 0.5;
                da.DecelerationRatio = 0.5;
            }
            transitionEffect.BeginAnimation(TransitionEffect.ProgressProperty, da);

            var oldVisualBrush = new VisualBrush(oldContentVisual);
            transitionEffect.OldImage = oldVisualBrush;
            SetNonVisualChild(newContent);
            ApplyEffect(transitionEffect);
        }

        /// <summary>
        /// Retrives the first visual child of the current control
        /// </summary>
        /// <returns></returns>
        private FrameworkElement GetVisualChild()
        {
            return VisualTreeHelper.GetChildrenCount(_contentPresenter) == 0 ? null : VisualTreeHelper.GetChild(_contentPresenter, 0) as FrameworkElement;
        }

        /// <summary>
        /// Sets the non visual child (data) of the control
        /// </summary>
        /// <param name="content">The non-visual content</param>
        private void SetNonVisualChild(object content)
        {
            _contentPresenter.Content = content;
        }

        /// <summary>
        /// Applies the specified transition effect
        /// </summary>
        /// <param name="effect">The transition effect to apply</param>
        private void ApplyEffect(TransitionEffect effect)
        {
            _contentPresenter.Effect = effect;
        }

        /// <summary>
        /// Executes when the template is applied to the current control
        /// </summary>
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            _contentPresenter = (ContentPresenter)Template.FindName("PART_ContentHost", this);
        }
    }
}


The control is based on article found on the internet. I often use the ContentControl so that insted of implementing the ContentTemplateSelector, I just create a class derived from the ContentControl and than override the OnContentChanged method where I select the appropriate ContentTemplate and than call the base.OnContentChanged method. So I tried the same approach here and it does'n work - the views change but the transition is not animated. I tried to mark the OnContentChanged method in TransitionControl with "new" keyword so the derived class would override the correct method but it did not help. After debugging I found out that the GetVisualChild() method always returns null and so the transition is not animated. If I don't override the TransitionControl and just use it in xaml and implement an appropriate ContentTemplateSelector the control work Ok, but I am still wondering what the problem is with the approach described above. Any ideas will be appreciated.

Uros
Posted

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



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900