/// <summary>
        /// Handles the Measure pass during Layout
        /// </summary>
        /// <param name="availableSize">Available size</param>
        /// <returns>Total Size required to accommodate all the Children</returns>
        protected override Size MeasureOverride(Size availableSize)
        {
            // Compositor will be null the very first time
            if (_compositor == null)
            {
                InitializeComposition();
            }

            // Clear any previously uninitialized items
            _uninitializedFluidItems.Clear();

            var availableItemSize = new Size(Double.PositiveInfinity, Double.PositiveInfinity);

            // Iterate through all the UIElements in the Children collection
            foreach (var child in Children.Where(c => c != null))
            {
                // Ask the child how much size it needs
                child.Measure(availableItemSize);
                // Check if the child is already added to the fluidElements collection
                if (FluidItems.Contains(child))
                    continue;

                // If the FluidItems collection does not contain this child it means it is newly
                // added to the FluidWrapPanel and is not initialized yet
                // Add the child to the fluidElements collection
                FluidItems.Add(child);
                // Add the child to the UninitializedFluidItems
                _uninitializedFluidItems.Add(child);
                // Get the visual of the child
                var visual = ElementCompositionPreview.GetElementVisual(child);

                visual.ImplicitAnimations = _implicitAnimationCollection;
                visual.CenterPoint = new Vector3((float)(child.DesiredSize.Width / 2), (float)(child.DesiredSize.Height / 2), 0);
                visual.Offset = new Vector3((float)-child.DesiredSize.Width, (float)-child.DesiredSize.Height, 0);
                _fluidVisuals[child] = visual;
            }

            // Unit size of a cell
            var cellSize = new Size(ItemWidth, ItemHeight);

            if ((availableSize.Width < 0.0d) || (availableSize.Width.IsZero())
                || (availableSize.Height < 0.0d) || (availableSize.Height.IsZero())
                || !FluidItems.Any())
            {
                return cellSize;
            }

            // Calculate how many unit cells can fit in the given width (or height) when the 
            // Orientation is Horizontal (or Vertical)
            _cellsPerLine = CalculateCellsPerLine(availableSize, cellSize, Orientation);

            // Convert the children's dimensions from Size to BitSize
            var childData = FluidItems.Select(child => new BitSize
            {
                Width = Math.Max(1, (int)Math.Floor((child.DesiredSize.Width / cellSize.Width) + 0.5)),
                Height = Math.Max(1, (int)Math.Floor((child.DesiredSize.Height / cellSize.Height) + 0.5))
            }).ToList();

            // If all the children have the same size as the cellSize then use optimized code
            // when a child is being dragged
            _isOptimized = !childData.Any(c => (c.Width != 1) || (c.Height != 1));

            int matrixWidth;
            int matrixHeight;
            if (Orientation == Orientation.Horizontal)
            {
                // If the maximum width required by a child is more than the calculated cellsPerLine, then
                // the matrix width should be the maximum width of that child
                matrixWidth = Math.Max(childData.Max(s => s.Width), _cellsPerLine);
                // For purpose of calculating the true size of the panel, the height of the matrix must
                // be set to the cumulative height of all the children
                matrixHeight = childData.Sum(s => s.Height);
            }
            else
            {
                // For purpose of calculating the true size of the panel, the width of the matrix must
                // be set to the cumulative width of all the children
                matrixWidth = childData.Sum(s => s.Width);
                // If the maximum height required by a child is more than the calculated cellsPerLine, then
                // the matrix height should be the maximum height of that child
                matrixHeight = Math.Max(childData.Max(s => s.Height), _cellsPerLine);
            }

            // Create FluidBitMatrix to calculate the size required by the panel
            var matrix = new FluidBitMatrix(matrixHeight, matrixWidth, Orientation);

            var startIndex = 0L;

            foreach (var child in childData)
            {
                var width = child.Width;
                var height = child.Height;

                MatrixCell cell;
                if (matrix.TryFindRegion(startIndex, width, height, out cell))
                {
                    matrix.SetRegion(cell, width, height);
                }
                else
                {
                    // If code reached here, it means that the child is too big to be accommodated
                    // in the matrix. Normally this should not occur!
                    throw new InvalidOperationException("Measure Pass: Unable to accommodate child in the panel!");
                }

                if (!OptimizeChildPlacement)
                {
                    // Update the startIndex so that the next child occupies a location which has 
                    // the same (or greater) row and/or column as this child
                    startIndex = (Orientation == Orientation.Horizontal) ? cell.Row : cell.Col;
                }
            }

            // Calculate the true size of the matrix
            var matrixSize = matrix.GetFilledMatrixDimensions();
            // Calculate the size required by the panel
            return new Size(matrixSize.Width * cellSize.Width, matrixSize.Height * cellSize.Height);
        }
        /// <summary>
        /// Handles the Arrange pass during Layout
        /// </summary>
        /// <param name="finalSize">Final Size of the control</param>
        /// <returns>Total size occupied by all the Children</returns>
        protected override Size ArrangeOverride(Size finalSize)
        {
            var cellSize = new Size(ItemWidth, ItemHeight);

            if ((finalSize.Width < 0.0d) || (finalSize.Width.IsZero())
                || (finalSize.Height < 0.0d) || (finalSize.Height.IsZero()))
            {
                finalSize = cellSize;
            }

            // Final size of the FluidWrapPanel
            _panelSize = finalSize;

            if (!FluidItems.Any())
            {
                return finalSize;
            }

            // Create the animation for the uinitialized children
            var offsetAnimation = _compositor.CreateVector3KeyFrameAnimation();
            offsetAnimation.Duration = InitializationAnimationDuration;

            // Calculate how many unit cells can fit in the given width (or height) when the 
            // Orientation is Horizontal (or Vertical)
            _cellsPerLine = CalculateCellsPerLine(finalSize, cellSize, Orientation);
            // Convert the children's dimensions from Size to BitSize
            var childData = FluidItems.ToDictionary(child => child, child => new BitSize
            {
                Width = Math.Max(1, (int)Math.Floor((child.DesiredSize.Width / cellSize.Width) + 0.5)),
                Height = Math.Max(1, (int)Math.Floor((child.DesiredSize.Height / cellSize.Height) + 0.5))
            });

            // If all the children have the same size as the cellSize then use optimized code
            // when a child is being dragged
            _isOptimized = !childData.Values.Any(c => (c.Width != 1) || (c.Height != 1));

            // Calculate matrix dimensions
            int matrixWidth;
            int matrixHeight;
            if (Orientation == Orientation.Horizontal)
            {
                // If the maximum width required by a child is more than the calculated cellsPerLine, then
                // the matrix width should be the maximum width of that child
                matrixWidth = Math.Max(childData.Values.Max(s => s.Width), _cellsPerLine);
                // For purpose of calculating the true size of the panel, the height of the matrix must
                // be set to the cumulative height of all the children
                matrixHeight = childData.Values.Sum(s => s.Height);
            }
            else
            {
                // For purpose of calculating the true size of the panel, the width of the matrix must
                // be set to the cumulative width of all the children
                matrixWidth = childData.Values.Sum(s => s.Width);
                // If the maximum height required by a child is more than the calculated cellsPerLine, then
                // the matrix height should be the maximum height of that child
                matrixHeight = Math.Max(childData.Values.Max(s => s.Height), _cellsPerLine);
            }

            // Create FluidBitMatrix to calculate the size required by the panel
            var matrix = new FluidBitMatrix(matrixHeight, matrixWidth, Orientation);

            var startIndex = 0L;
            _fluidBits.Clear();

            foreach (var child in childData)
            {
                var width = child.Value.Width;
                var height = child.Value.Height;

                MatrixCell cell;
                if (matrix.TryFindRegion(startIndex, width, height, out cell))
                {
                    // Set the bits
                    matrix.SetRegion(cell, width, height);
                    // Arrange the child
                    child.Key.Arrange(new Rect(0, 0, child.Key.DesiredSize.Width, child.Key.DesiredSize.Height));
                    // Convert MatrixCell location to actual location
                    var pos = new Vector3((float)(cell.Col * cellSize.Width), (float)(cell.Row * cellSize.Height), 0);
                    // Get the Bit Information for this child
                    BitInfo info;
                    info.Row = cell.Row;
                    info.Col = cell.Col;
                    info.Width = width;
                    info.Height = height;
                    _fluidBits.Add(child.Key, info);

                    // If this child is not being dragged, then set the Offset of its visual
                    if (!ReferenceEquals(child.Key, _dragElement))
                    {
                        var visual = _fluidVisuals[child.Key];
                        // Is the child unitialized?
                        if (_uninitializedFluidItems.Contains(child.Key))
                        {
                            // Use explicit animation to position the uninitialized child to the new location
                            // because implicit property animations do not run the first time 
                            // a Visual shows up on screen
                            offsetAnimation.InsertKeyFrame(1f, pos);
                            visual.StartAnimation(() => visual.Offset, offsetAnimation);
                        }
                        else
                        {
                            // Child has been already initialized. Set the Offset directly. The ImplicitAnimations
                            // of the Child's visual will take care of animating it to the new location
                            _fluidVisuals[child.Key].Offset = pos;
                        }
                    }
                }
                else
                {
                    // If code reached here, it means that the child is too big to be accommodated
                    // in the matrix. Normally this should not occur!
                    throw new InvalidOperationException("Arrange Pass: Unable to accommodate child in the panel!");
                }

                if (!OptimizeChildPlacement)
                {
                    // Update the startIndex so that the next child occupies a location which has 
                    // the same (or greater) row and/or column as this child
                    startIndex = (Orientation == Orientation.Horizontal) ? cell.Row : cell.Col;
                }
            }

            // All the uninitialized fluid items have been initialized, so clear the list
            _uninitializedFluidItems.Clear();

            // Calculate the maximum cells along the width and height of the FluidWrapPanel
            _maxCellRows = (int)Math.Max(1, Math.Floor(_panelSize.Height / ItemHeight));
            _maxCellCols = (int)Math.Max(1, Math.Floor(_panelSize.Width / ItemWidth));

            return finalSize;
        }
        /// <summary>
        /// Handles the situation when the user drags a dragElement which does not have 
        /// unit size dimension. It checks if the dragElement can fit in the new location and
        /// the rest of the children can be rearranged successfully in the remaining space.
        /// </summary>
        /// <param name="position">Position of the pointer within the dragElement</param>
        /// <param name="positionInParent">Position of the pointer w.r.t. the FluidWrapPanel</param>
        /// <returns>True if successful otherwise False</returns>
        private bool TryFluidDrag(Point position, Point positionInParent)
        {
            // Get the index of the dragElement
            var dragCellIndex = FluidItems.IndexOf(_dragElement);

            // Convert the current location to MatrixCell which indicates the top left cell of the dragElement
            var currentCell = GetCellFromPoint(_fluidBits[_dragElement], position, positionInParent);

            // Check if the item being dragged can fit in the new cell location
            if (!IsValidCellPosition(_dragElement, currentCell))
                return false;

            // Get the list of cells vacated when the dragElement moves to the new cell location
            var vacatedCells = GetVacatedCells(_dragElement, currentCell);
            // If none of the cells are vacated, no need to proceed further
            if (vacatedCells.Count == 0)
            {
                _lastExchangedElement = null;
                return false;
            }

            // Get the list of children overlapped by the 
            var overlappedChildren = GetOverlappedChildren(_dragElement, currentCell);
            var dragInfo = _fluidBits[_dragElement];
            // If there is only one overlapped child and its dimension matches the 
            // dimensions of the dragElement, then exchange their indices
            if (overlappedChildren.Count == 1)
            {
                var element = overlappedChildren[0];
                var info = _fluidBits[element];
                var dragCellCount = info.Width * info.Height;
                if ((info.Width == dragInfo.Width) && (info.Height == dragInfo.Height))
                {
                    // If user moves the dragElement back to the lastExchangedElement's position, then it can
                    // be exchanged again only if the dragElement has vacated all the cells occupied by it in 
                    // the previous location.
                    if (ReferenceEquals(element, _lastExchangedElement) && (vacatedCells.Count != dragCellCount))
                    {
                        return false;
                    }

                    // Exchange the dragElement and the overlapped element
                    _lastExchangedElement = element;
                    var index = FluidItems.IndexOf(element);
                    // To prevent an IndexOutOfRangeException during the exchange
                    // Remove the item with higher index first followed by the lower index item and then
                    // Insert the items in the lower index first and then in the higher index
                    if (index > dragCellIndex)
                    {
                        FluidItems.RemoveAt(index);
                        FluidItems.RemoveAt(dragCellIndex);
                        FluidItems.Insert(dragCellIndex, element);
                        FluidItems.Insert(index, _dragElement);
                    }
                    else
                    {
                        FluidItems.RemoveAt(dragCellIndex);
                        FluidItems.RemoveAt(index);
                        FluidItems.Insert(index, _dragElement);
                        FluidItems.Insert(dragCellIndex, element);
                    }

                    return true;
                }
            }

            // Since there are multiple overlapped children, we need to rearrange all the children
            // Create a temporary matrix to check if all the children are placed successfully
            // when the dragElement is moved to the new cell location
            var tempMatrix = new FluidBitMatrix(_maxCellRows, _maxCellCols, Orientation);

            // First set the cells corresponding to dragElement's cells in new location
            tempMatrix.SetRegion(currentCell, dragInfo.Width, dragInfo.Height);
            // Try to fit the remaining items
            var startIndex = 0L;
            var tempFluidBits = new Dictionary<UIElement, BitInfo>();
            // Add the new bit information for dragElement
            dragInfo.Row = currentCell.Row;
            dragInfo.Col = currentCell.Col;
            tempFluidBits[_dragElement] = dragInfo;

            // Try placing the rest of the children in the matrix
            foreach (var item in _fluidBits.Where(t => !ReferenceEquals(t.Key, _dragElement)))
            {
                var width = item.Value.Width;
                var height = item.Value.Height;

                MatrixCell cell;
                if (tempMatrix.TryFindRegion(startIndex, width, height, out cell))
                {
                    // Set the bits
                    tempMatrix.SetRegion(cell, width, height);
                    // Capture the bit information
                    BitInfo newinfo;
                    newinfo.Row = cell.Row;
                    newinfo.Col = cell.Col;
                    newinfo.Width = width;
                    newinfo.Height = height;
                    tempFluidBits.Add(item.Key, newinfo);
                }
                else
                {
                    // No suitable location was found to fit the current item. So the children cannot be
                    // successfully placed after moving dragElement to new cell location. So dragElement
                    // will not be moved.
                    return false;
                }

                // Update the startIndex so that the next child occupies a location the same (or greater)
                // row and/or column as this child
                if (!OptimizeChildPlacement)
                {
                    startIndex = (Orientation == Orientation.Horizontal) ? cell.Row : cell.Col;
                }
            }

            // All the children have been successfully readjusted, so now 
            // Re-Index the children based on the panel's orientation
            var tempFluidItems = new List<UIElement>();
            if (Orientation == Orientation.Horizontal)
            {
                for (var row = 0; row < _maxCellRows; row++)
                {
                    for (var col = 0; col < _maxCellCols; col++)
                    {
                        var item = tempFluidBits.Where(t => t.Value.Contains(row, col))
                                                .Select(t => t.Key).FirstOrDefault();
                        if ((item != null) && (!tempFluidItems.Contains(item)))
                        {
                            tempFluidItems.Add(item);
                        }
                    }
                }
            }
            else
            {
                for (var col = 0; col < _maxCellCols; col++)
                {
                    for (var row = 0; row < _maxCellRows; row++)
                    {
                        var item = tempFluidBits.Where(t => t.Value.Contains(row, col))
                                                .Select(t => t.Key).FirstOrDefault();
                        if ((item != null) && (!tempFluidItems.Contains(item)))
                        {
                            tempFluidItems.Add(item);
                        }
                    }
                }
            }

            // Update the new indices in FluidItems
            FluidItems.Clear();
            foreach (var fluidItem in tempFluidItems)
            {
                FluidItems.Add(fluidItem);
            }

            // Clean up
            tempFluidItems.Clear();
            tempFluidBits.Clear();

            return true;
        }