/// <summary> /// Handler for the event when the user drags the dragElement. /// </summary> /// <param name="child">UIElement being dragged</param> /// <param name="position">Position where the user clicked w.r.t. the UIElement being dragged</param> /// <param name="positionInParent">Position where the user clicked w.r.t. the FluidWrapPanel (the parentFWPanel of the UIElement being dragged</param> internal async Task FluidDragAsync(UIElement child, Point position, Point positionInParent) { if ((child == null) || (!IsComposing) || (_dragElement == null)) { return; } // Call the event handler core on the Dispatcher. (Improves efficiency!) await Dispatcher.InvokeAsync(() => { _dragElement.RenderTransform = CreateTransform(positionInParent.X - _dragStartPoint.X, positionInParent.Y - _dragStartPoint.Y, DragScale, DragScale); // Get the index in the fluidElements list corresponding to the current mouse location var currentPt = positionInParent; var index = GetIndexFromPoint(currentPt, _dragElement, FluidItems); //if (index == _originalDragIndex) // return; Debug.WriteLine( $"Current Pt: {currentPt.ToString()} OldIndex: {FluidItems.IndexOf(_dragElement)} NewIndex: {index}"); // If no valid cell index is obtained, add the child to the end of the // fluidElements list. if ((index == -1) || (index >= FluidItems.Count)) { index = FluidItems.Count - 1; } var element = FluidItems[index]; if (_dragElement == element) { _lastExchangeElement = null; return; } if (element == _lastExchangeElement) { return; } _lastExchangeElement = element; var dragCellIndex = FluidItems.IndexOf(_dragElement); FluidItems.RemoveAt(dragCellIndex); FluidItems.Insert(index, _dragElement); Debug.WriteLine("Invalidating"); InvalidateVisual(); }); }
/// <summary> /// Removes all the children from the FluidWrapPanel /// </summary> private void ClearItemsSource() { FluidItems.Clear(); Children.Clear(); }
/// <summary> /// Arrange Phase /// </summary> /// <param name="finalSize">Final Size inside which the panel and its children must be arranged</param> /// <returns>Size consumed by the panel</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; } _panelSize = finalSize; if (!FluidItems.Any()) { return(finalSize); } // 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)) }).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.Value.Width != 1) || (c.Value.Height != 1)); // Calculate matrix dimensions var matrixWidth = 0; var matrixHeight = 0; 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.Value.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.Value.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.Value.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.Value.Height), _cellsPerLine); } // Create FluidBitMatrix to calculate the size required by the panel var matrix = new FluidBitMatrix(matrixHeight, matrixWidth, Orientation); var startIndex = 0L; _bounds.Clear(); foreach (var child in childData) { var location = matrix.FindRegion(startIndex, child.Value.Width, child.Value.Height); if (location.IsValid()) { // Set the bits matrix.SetRegion(location, child.Value.Width, child.Value.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 Point(location.Col * cellSize.Width, location.Row * cellSize.Height); _bounds[child.Key] = new Rect(pos, child.Key.DesiredSize); if (child.Key != _dragElement) { // Animate the child to the new location CreateTransitionAnimation(child.Key, pos); } } // 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) ? location.Row : location.Col; } } return(finalSize); }
/// <summary> /// Measure Phase /// </summary> /// <param name="availableSize">Available Size</param> /// <returns>Size required by the panel</returns> protected override Size MeasureOverride(Size availableSize) { var availableItemSize = new Size(Double.PositiveInfinity, Double.PositiveInfinity); // Iterate through all the UIElements in the Children collection for (var i = 0; i < Children.Count; i++) { var child = Children[i]; if (child == null) { continue; } // 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; } // Add the child to the fluidElements collection FluidItems.Add(child); // Initialize its RenderTransform child.RenderTransform = CreateTransform(-ItemWidth, -ItemHeight, NORMAL_SCALE, NORMAL_SCALE); } 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)); var matrixWidth = 0; var matrixHeight = 0; 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 location = matrix.FindRegion(startIndex, child.Width, child.Height); if (location.IsValid()) { matrix.SetRegion(location, child.Width, child.Height); } // 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) ? location.Row : location.Col; } } var matrixSize = matrix.GetFilledMatrixDimensions(); return(new Size(matrixSize.Width * cellSize.Width, matrixSize.Height * cellSize.Height)); }