/// <summary>
        /// Perform animation effects on the set of children.
        /// </summary>
        /// <param name="animateId">Identifier of the animate to be used.</param>
        /// <param name="metaPanel">Reference to owning panel instance.</param>
        /// <param name="stateDict">Dictionary of per-element state.</param>
        /// <param name="elements">Collection of elements to be animated.</param>
        /// <param name="elapsedMilliseconds">Elapsed milliseconds since last animation cycle.</param>
        public override void ApplyAnimation(string animateId,
                                            MetaPanelBase metaPanel,
                                            MetaElementStateDict stateDict,
                                            ICollection elements,
                                            double elapsedMilliseconds)
        {
            // Only apply if we match the incoming animate identifier
            if (string.IsNullOrEmpty(Id) || Id.Equals(animateId))
            {
                foreach (UIElement element in elements)
                {
                    MetaElementState elementState = stateDict[element];

                    // Only interested in elements being removed...
                    if (elementState.Status == MetaElementStatus.Removing)
                    {
                        // ...and having the final target rectangle calculated just the once...
                        if (!elementState.RemoveCalculated)
                        {
                            // Calculate the correct target rectangle
                            Rect positionRect = RectFromSize(Size, RectFromLocation(Location, metaPanel, elementState), elementState);

                            // Update the final target value
                            elementState.TargetRect      = positionRect;
                            elementState.TargetChanged   = true;
                            elementState.AnimateComplete = false;
                        }
                    }
                }
            }

            // Let base class take care of easing animations
            base.ApplyAnimation(animateId, metaPanel, stateDict, elements, elapsedMilliseconds);
        }
Пример #2
0
        /// <summary>
        /// Perform animation effects on the set of children.
        /// </summary>
        /// <param name="animateId">Identifier of the animate to be used.</param>
        /// <param name="metaPanel">Reference to owning panel instance.</param>
        /// <param name="stateDict">Dictionary of per-element state.</param>
        /// <param name="elements">Collection of elements to be animated.</param>
        /// <param name="elapsedMilliseconds">Elapsed milliseconds since last animation cycle.</param>
        public override void ApplyAnimation(string animateId,
                                            MetaPanelBase metaPanel,
                                            MetaElementStateDict stateDict,
                                            ICollection elements,
                                            double elapsedMilliseconds)
        {
            // Only apply if we match the incoming animate identifier
            if (string.IsNullOrEmpty(Id) || Id.Equals(animateId))
            {
                foreach (UIElement element in elements)
                {
                    MetaElementState elementState = stateDict[element];

                    // If this is a new element that has not had its current rectangle set
                    if ((elementState.Status == MetaElementStatus.New) && elementState.NewCalculating)
                    {
                        elementState.CurrentRect     = RectFromSize(Size, RectFromLocation(Location, metaPanel, elementState), elementState);
                        elementState.TargetChanged   = true;
                        elementState.AnimateComplete = false;
                    }
                }
            }

            // Let base class take care of easing animations
            base.ApplyAnimation(animateId, metaPanel, stateDict, elements, elapsedMilliseconds);
        }
Пример #3
0
        /// <summary>
        /// Perform animation effects on the set of children.
        /// </summary>
        /// <param name="animateId">Identifier of the animate to be used.</param>
        /// <param name="metaPanel">Reference to owning panel instance.</param>
        /// <param name="stateDict">Dictionary of per-element state.</param>
        /// <param name="elements">Collection of elements to be animated.</param>
        /// <param name="elapsedMilliseconds">Elapsed milliseconds since last animation cycle.</param>
        /// <param name="startOpacity">Opacity for start of animation.</param>
        /// <param name="endOpacity">Opacity for end of animation.</param>
        public void ApplyAnimation(string animateId,
                                   MetaPanelBase metaPanel,
                                   MetaElementStateDict stateDict,
                                   ICollection elements,
                                   double elapsedMilliseconds,
                                   double startOpacity,
                                   double endOpacity)
        {
            // Only apply if we match the incoming animate identifier
            if (string.IsNullOrEmpty(Id) || Id.Equals(animateId))
            {
                // Only grab the dependancy properties once, for improved perf
                double duration = Duration;

                foreach (UIElement element in elements)
                {
                    // Only apply animation to element of required target state
                    MetaElementState elementState = stateDict[element];
                    if (elementState.Status == Target)
                    {
                        // If the element is the correct target and correct status for starting animation
                        if ((!elementState.RemoveCalculated && (Target == MetaElementStatus.Removing)) ||
                            (elementState.NewCalculating && (Target == MetaElementStatus.New)))
                        {
                            // Use the starting opacity
                            element.Opacity = startOpacity;
                            elementState.ElapsedOpacityTime = 0;
                        }
                        else
                        {
                            // Only perform animation if some time has actually ellapsed and not already at target
                            if ((elapsedMilliseconds > 0) && (element.Opacity != (double)endOpacity))
                            {
                                // Add new elapsed time to the animation running time
                                elementState.ElapsedOpacityTime += elapsedMilliseconds;

                                // Does elapsed time indicate animation should have completed?
                                if (elementState.ElapsedOpacityTime >= duration)
                                {
                                    element.Opacity = endOpacity;
                                }
                                else
                                {
                                    element.Opacity = EasingCalculation.Calculate(elementState.ElapsedOpacityTime, startOpacity, endOpacity - startOpacity, duration);
                                }
                            }
                        }

                        // If not yet at target opacity then not finished with animating
                        elementState.AnimateComplete &= (element.Opacity == (double)endOpacity);
                    }
                }
            }
        }
Пример #4
0
        private void OnUIElementsRemove(object sender, UIElementsEventArgs e)
        {
            foreach (UIElement element in e.Elements)
            {
                MetaElementState elementState = _stateDict[element];
                elementState.Status        = MetaElementStatus.Removing;
                elementState.TargetChanged = true;
            }

            // If we are animating the removal of an element then it might not cause a
            // measure to occur because we do not actually remove it from the visual
            // collection. So force measure here so animation will be started.
            InvalidateMeasure();
        }
Пример #5
0
        /// <summary>
        /// Calculate appropriate rectangle from given current state and target size.
        /// </summary>
        /// <param name="size">Size enumeration.</param>
        /// <param name="rect">Rectangle to modify.</param>
        /// <param name="elementState">Animation state of element.</param>
        /// <returns>Calculated rectangle using provided size.</returns>
        protected Rect RectFromSize(AnimateSize size,
                                    Rect rect,
                                    MetaElementState elementState)
        {
            Size minSize = elementState.Element.DesiredSize;

            switch (size)
            {
            case AnimateSize.Original:
                return(rect);

            case AnimateSize.ZeroWidthLeft:
                return(new Rect(rect.X, rect.Y, minSize.Width, rect.Height));

            case AnimateSize.ZeroWidthCenter:
                return(new Rect(rect.X + ((rect.Width - minSize.Width) / 2), rect.Y, minSize.Width, rect.Height));

            case AnimateSize.ZeroWidthRight:
                return(new Rect(rect.Right - minSize.Width, rect.Y, minSize.Width, rect.Height));

            case AnimateSize.ZeroHeightTop:
                return(new Rect(rect.X, rect.Y, rect.Width, minSize.Height));

            case AnimateSize.ZeroHeightCenter:
                return(new Rect(rect.X, rect.Y + (rect.Height - minSize.Height) / 2, rect.Width, minSize.Height));

            case AnimateSize.ZeroHeightBottom:
                return(new Rect(rect.X, rect.Bottom - minSize.Height, rect.Width, minSize.Height));

            case AnimateSize.ZeroZeroCenter:
                return(new Rect(rect.X + (rect.Width - minSize.Width) / 2, rect.Y + (rect.Height - minSize.Height) / 2, minSize.Width, minSize.Height));

            case AnimateSize.ZeroZeroTopLeft:
                return(new Rect(rect.X, rect.Y, minSize.Width, minSize.Height));

            case AnimateSize.ZeroZeroTopRight:
                return(new Rect(rect.Right - minSize.Width, rect.Y, minSize.Width, minSize.Height));

            case AnimateSize.ZeroZeroBottomLeft:
                return(new Rect(rect.X, rect.Bottom - minSize.Height, minSize.Width, minSize.Height));

            case AnimateSize.ZeroZeroBottomRight:
                return(new Rect(rect.Right - minSize.Width, rect.Bottom - minSize.Height, minSize.Width, minSize.Height));

            default:
                // Should never happen!
                Debug.Assert(false);
                return(new Rect());
            }
        }
Пример #6
0
        /// <summary>
        /// Position child elements according to already calculated target state.
        /// </summary>
        /// <param name="layoutId">Identifier of the layout to be used.</param>
        /// <param name="metaPanel">Reference to owning panel instance.</param>
        /// <param name="stateDict">Dictionary of per-element state.</param>
        /// <param name="elements">Collection of elements to be arranged.</param>
        /// <param name="finalSize">Size that layout should use to arrange child elements.</param>
        /// <returns>Size used by the layout panel.</returns>
        public Size ArrangeChildren(string layoutId,
                                    MetaPanelBase metaPanel,
                                    MetaElementStateDict stateDict,
                                    ICollection elements,
                                    Size finalSize)
        {
            foreach (UIElement element in elements)
            {
                MetaElementState elementState = stateDict[element];
                elementState.Element.Arrange(elementState.CurrentRect);
            }

            // Layout panel takes all the provided size
            return(finalSize);
        }
Пример #7
0
 /// <summary>
 /// Perform animation effects on the set of children.
 /// </summary>
 /// <param name="animateId">Identifier of the animate to be used.</param>
 /// <param name="metaPanel">Reference to owning panel instance.</param>
 /// <param name="stateDict">Dictionary of per-element state.</param>
 /// <param name="elements">Collection of elements to be animated.</param>
 /// <param name="elapsedMilliseconds">Elapsed milliseconds since last animation cycle.</param>
 public override void ApplyAnimation(string animateId,
                                     MetaPanelBase metaPanel,
                                     MetaElementStateDict stateDict,
                                     ICollection elements,
                                     double elapsedMilliseconds)
 {
     // Only apply if we match the incoming animate identifier
     if (string.IsNullOrEmpty(Id) || Id.Equals(animateId))
     {
         foreach (UIElement element in elements)
         {
             // Immediately move to the target rectangle
             MetaElementState elementState = stateDict[element];
             elementState.CurrentRect = elementState.TargetRect;
         }
     }
 }
Пример #8
0
        /// <summary>
        /// Invoked when the VisualCollection of a visual object is modified.
        /// </summary>
        /// <param name="visualAdded">The Visual that was added to the collection.</param>
        /// <param name="visualRemoved">The Visual that was removed from the collection.</param>
        protected override void OnVisualChildrenChanged(DependencyObject visualAdded, DependencyObject visualRemoved)
        {
            if (!_ignoreVisualChange)
            {
                // Let base class do its own stuff
                base.OnVisualChildrenChanged(visualAdded, visualRemoved);

                // When an items host we do not get the UIElementAdded/Removed events
                // for changing the element state dictionary. So we do it here instead.
                if (IsItemsHost)
                {
                    if (visualAdded is UIElement)
                    {
                        UIElement elementAdded = (UIElement)visualAdded;
                        _stateDict.Add(elementAdded, new MetaElementState(elementAdded));
                    }

                    if (visualRemoved is UIElement)
                    {
                        UIElement        elementRemoved = (UIElement)visualRemoved;
                        MetaElementState elementState   = _stateDict[elementRemoved];

                        // If item has finished its removal
                        if (elementState.Status == MetaElementStatus.Removing)
                        {
                            // Base class already removed it as a visual/logical child so just remove dictionary entry
                            _stateDict.Remove(elementRemoved);
                        }
                        else
                        {
                            // Item needs marking so it removal animates
                            elementState.Status        = MetaElementStatus.Removing;
                            elementState.TargetChanged = true;

                            // Prevent reentrancy from trying to process the element being added back again
                            _ignoreVisualChange = true;

                            // Add into the internal collection and add back as a visual child
                            _children.InternalAdd(elementRemoved);
                            _ignoreVisualChange = false;
                        }
                    }
                }
            }
        }
Пример #9
0
        /// <summary>
        /// Perform remove animation for the child and then remove from the children.
        /// </summary>
        /// <param name="element">Element to be removed.</param>
        public void RemoveChild(UIElement element)
        {
            // Check the element is in the collection
            if (_stateDict.ContainsKey(element))
            {
                // If the element is not already marked to be removed...
                MetaElementState elementState = _stateDict[element];
                if (elementState.Status != MetaElementStatus.Removing)
                {
                    // Mark element to be removed
                    elementState.Status        = MetaElementStatus.Removing;
                    elementState.TargetChanged = true;

                    // Need to measure to force remove animation
                    InvalidateMeasure();
                }
            }
        }
Пример #10
0
        /// <summary>
        /// Position child elements according to already calculated target state.
        /// </summary>
        /// <param name="layoutId">Identifier of the layout to be used.</param>
        /// <param name="metaPanel">Reference to owning panel instance.</param>
        /// <param name="stateDict">Dictionary of per-element state.</param>
        /// <param name="elements">Collection of elements to be arranged.</param>
        /// <param name="finalSize">Size that layout should use to arrange child elements.</param>
        /// <returns>Size used by the layout panel.</returns>
        public virtual Size ArrangeChildren(string layoutId,
                                            MetaPanelBase metaPanel,
                                            MetaElementStateDict stateDict,
                                            ICollection elements,
                                            Size finalSize)
        {
            // Only apply if we match the incoming layout identifier
            if (string.IsNullOrEmpty(Id) || Id.Equals(layoutId))
            {
                // Position each element
                foreach (UIElement element in elements)
                {
                    MetaElementState elementState = stateDict[element];
                    elementState.Element.Arrange(elementState.CurrentRect);
                }
            }

            // Layout panel takes all the provided size
            return(finalSize);
        }
Пример #11
0
        /// <summary>
        /// Perform animation effects on the set of children.
        /// </summary>
        /// <param name="animateId">Identifier of the animate to be used.</param>
        /// <param name="metaPanel">Reference to owning panel instance.</param>
        /// <param name="stateDict">Dictionary of per-element state.</param>
        /// <param name="elements">Collection of elements to be animated.</param>
        /// <param name="elapsedMilliseconds">Elapsed milliseconds since last animation cycle.</param>
        public override void ApplyAnimation(string animateId,
                                            MetaPanelBase metaPanel,
                                            MetaElementStateDict stateDict,
                                            ICollection elements,
                                            double elapsedMilliseconds)
        {
            // Only apply if we match the incoming animate identifier
            if (string.IsNullOrEmpty(Id) || Id.Equals(animateId))
            {
                // Update the easing equation with latest value
                EasingCalculation.Easing = Easing;

                // Cache dependancy properties for faster perf
                double duration = Math.Max((double)1, Duration);

                foreach (UIElement element in elements)
                {
                    // Only apply animation to element of required target state
                    MetaElementState elementState = stateDict[element];
                    if (elementState.Status == Target)
                    {
                        Rect targetRect  = elementState.TargetRect;
                        Rect currentRect = (elementState.CurrentRect.IsEmpty ? targetRect : elementState.CurrentRect);

                        // If start of animation....
                        if (elementState.TargetChanged)
                        {
                            // Cache starting information
                            elementState.StartRect         = currentRect;
                            elementState.ElapsedBoundsTime = 0;
                        }
                        else
                        {
                            // Only perform animation if some time has actually ellapsed and not already at target
                            if ((elapsedMilliseconds > 0) && !currentRect.Equals(targetRect))
                            {
                                // Add new elapsed time to the animation running time
                                elementState.ElapsedBoundsTime += elapsedMilliseconds;

                                // Does elapsed time indicate animation should have completed?
                                if (elementState.ElapsedBoundsTime >= duration)
                                {
                                    currentRect = targetRect;
                                }
                                else
                                {
                                    Rect   startRect   = elementState.StartRect;
                                    double elapsedTime = elementState.ElapsedBoundsTime;

                                    // Using animation easing to discover new target rectangle corners
                                    double left   = EasingCalculation.Calculate(elapsedTime, startRect.X, targetRect.X - startRect.X, duration);
                                    double top    = EasingCalculation.Calculate(elapsedTime, startRect.Y, targetRect.Y - startRect.Y, duration);
                                    double bottom = EasingCalculation.Calculate(elapsedTime, startRect.Bottom, targetRect.Bottom - startRect.Bottom, duration);
                                    double right  = EasingCalculation.Calculate(elapsedTime, startRect.Right, targetRect.Right - startRect.Right, duration);

                                    // Normalize edges left/right edges
                                    if (left > right)
                                    {
                                        elapsedTime = left;
                                        left        = right;
                                        right       = elapsedTime;
                                    }

                                    // Normalize edges top/bottom edges
                                    if (top > bottom)
                                    {
                                        elapsedTime = top;
                                        top         = bottom;
                                        bottom      = elapsedTime;
                                    }

                                    currentRect = new Rect(left, top, right - left, bottom - top);
                                }
                            }
                        }

                        // Put back the updated rectangle and decide if more animation is needed
                        elementState.CurrentRect      = currentRect;
                        elementState.AnimateComplete &= (currentRect.Equals(targetRect));
                    }
                }
            }
        }
Пример #12
0
        /// <summary>
        /// Calculate appropriate rectangle from given current state and target location.
        /// </summary>
        /// <param name="location">Location enumeration.</param>
        /// <param name="metaPanel">Reference to owning panel instance.</param>
        /// <param name="elementState">Animation state of element.</param>
        /// <returns>Calculated rectangle using provided location.</returns>
        protected Rect RectFromLocation(AnimateLocation location,
                                        MetaPanelBase metaPanel,
                                        MetaElementState elementState)
        {
            // Nearest edge is converted into a particular edge
            switch (location)
            {
            case AnimateLocation.NearestEdge:
            case AnimateLocation.NearestEdgePaged:
                bool paged = (location == AnimateLocation.NearestEdgePaged);

                // Find distance from each edge
                double left   = Math.Abs(elementState.TargetRect.Left);
                double top    = Math.Abs(elementState.TargetRect.Top);
                double right  = Math.Abs(metaPanel.ActualWidth - elementState.TargetRect.Right);
                double bottom = Math.Abs(metaPanel.ActualHeight - elementState.TargetRect.Bottom);

                // Find nearest distance for vertical and horizontal
                double horz = (left < right ? left : right);
                double vert = (top < bottom ? top : bottom);

                // Is horizontal nearest?
                if (horz <= vert)
                {
                    // Is the left the nearest?
                    if (horz == left)
                    {
                        location = (paged ? AnimateLocation.LeftPaged : AnimateLocation.Left);
                    }
                    else
                    {
                        location = (paged ? AnimateLocation.RightPaged : AnimateLocation.Right);
                    }
                }
                else
                {
                    // Is the top the nearest?
                    if (vert == top)
                    {
                        location = (paged ? AnimateLocation.TopPaged : AnimateLocation.Top);
                    }
                    else
                    {
                        location = (paged ? AnimateLocation.BottomPaged : AnimateLocation.Bottom);
                    }
                }
                break;
            }

            switch (location)
            {
            case AnimateLocation.Target:
                return(elementState.TargetRect);

            case AnimateLocation.Center:
                return(new Rect((metaPanel.ActualWidth / 2) - (elementState.TargetRect.Width - 2),
                                (metaPanel.ActualHeight / 2) - (elementState.TargetRect.Height - 2),
                                elementState.TargetRect.Width,
                                elementState.TargetRect.Height));

            case AnimateLocation.Top:
                return(new Rect(elementState.TargetRect.X,
                                -elementState.TargetRect.Height,
                                elementState.TargetRect.Width,
                                elementState.TargetRect.Height));

            case AnimateLocation.TopPaged:
                return(new Rect(elementState.TargetRect.X,
                                -metaPanel.ActualHeight + elementState.TargetRect.Y,
                                elementState.TargetRect.Width,
                                elementState.TargetRect.Height));

            case AnimateLocation.Bottom:
                return(new Rect(elementState.TargetRect.X,
                                metaPanel.ActualHeight,
                                elementState.TargetRect.Width,
                                elementState.TargetRect.Height));

            case AnimateLocation.BottomPaged:
                return(new Rect(elementState.TargetRect.X,
                                metaPanel.ActualHeight + elementState.TargetRect.Y,
                                elementState.TargetRect.Width,
                                elementState.TargetRect.Height));

            case AnimateLocation.Left:
                return(new Rect(-elementState.TargetRect.Width,
                                elementState.TargetRect.Y,
                                elementState.TargetRect.Width,
                                elementState.TargetRect.Height));

            case AnimateLocation.LeftPaged:
                return(new Rect(-metaPanel.ActualWidth + elementState.TargetRect.X,
                                elementState.TargetRect.Y,
                                elementState.TargetRect.Width,
                                elementState.TargetRect.Height));

            case AnimateLocation.Right:
                return(new Rect(metaPanel.ActualWidth,
                                elementState.TargetRect.Y,
                                elementState.TargetRect.Width,
                                elementState.TargetRect.Height));

            case AnimateLocation.RightPaged:
                return(new Rect(metaPanel.ActualWidth + elementState.TargetRect.X,
                                elementState.TargetRect.Y,
                                elementState.TargetRect.Width,
                                elementState.TargetRect.Height));

            case AnimateLocation.TopLeft:
                return(new Rect(-elementState.TargetRect.Width,
                                -elementState.TargetRect.Height,
                                elementState.TargetRect.Width,
                                elementState.TargetRect.Height));

            case AnimateLocation.TopRight:
                return(new Rect(metaPanel.ActualWidth,
                                -elementState.TargetRect.Height,
                                elementState.TargetRect.Width,
                                elementState.TargetRect.Height));

            case AnimateLocation.BottomLeft:
                return(new Rect(-elementState.TargetRect.Width,
                                metaPanel.ActualHeight,
                                elementState.TargetRect.Width,
                                elementState.TargetRect.Height));

            case AnimateLocation.BottomRight:
                return(new Rect(metaPanel.ActualWidth,
                                metaPanel.ActualHeight,
                                elementState.TargetRect.Width,
                                elementState.TargetRect.Height));

            default:
                // Should never happen!
                Debug.Assert(false);
                return(new Rect());
            }
        }