using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows.Controls; using System.Windows; using System.Collections; namespace PixelLab.ToolBox { public class ColumnWrapPanel : Panel { #region Fields  private int _columnCount = 0; double _columnWidth = 0; private int _maxRowCount = 0; private int _rowCount = 0; private bool[,] cells; #endregion Fields  #region Protected Methods  protected override Size ArrangeOverride(Size finalSize) { double top = 0; double left = 0; double width = 0; double height = 0; double totalheight = 0; foreach (UIElement element in this.Children) { Point location = (Point)element.GetValue(ComputedLocationProperty); left = (location.X * _columnWidth); top = (location.Y * RowHeightIncrement); width = Math.Min(((int)element.GetValue(ColumnSpanProperty) * _columnWidth), finalSize.Width); //height = element.DesiredSize.Height; height = Math.Ceiling(element.DesiredSize.Height / RowHeightIncrement) * RowHeightIncrement; if (top + height > totalheight) totalheight = top + height; element.Arrange(new Rect(left, top, width, height)); } return new Size(finalSize.Width, totalheight); } protected override Size MeasureOverride(Size availableSize) { // calculate the column width _columnWidth = this.MinColumnWidth; _columnCount = 1000; int maxRowSpan = 1; // compute the actual column widths (remeber that we just specified a minimum) if (!double.IsInfinity(availableSize.Width)) { _columnCount = Math.Max((int)Math.Floor(availableSize.Width / this.MinColumnWidth), 1); _columnWidth = (availableSize.Width / _columnCount) + ((availableSize.Width % _columnCount) / _columnCount); } // measure each of the children at a fixed width and unlimited height foreach (UIElement element in this.Children) { int columnSpan = (int)element.GetValue(ColumnSpanProperty); Size childAvailableSize = new Size(_columnWidth * columnSpan, availableSize.Height); element.Measure(childAvailableSize); int rowSpan = (int)Math.Ceiling(element.DesiredSize.Height / this.RowHeightIncrement); if (rowSpan > maxRowSpan) maxRowSpan = rowSpan; } // we should now be able to perform pseudo arrange on everything // (which is necessary in order to return a correct size) // initialize cells to be the right number of columns by the total // number of elements * maxRowSpan (which is the maximum number of // rows that all elements combined could span) _rowCount = 0; _maxRowCount = maxRowSpan * this.Children.Count; cells = new bool[_columnCount, _maxRowCount]; cells.Initialize(); double top = 0; double height = 0; double totalheight = 0; foreach (UIElement element in this.Children) { int columnSpan = (int)element.GetValue(ColumnSpanProperty); int rowSpan = (int)Math.Ceiling(element.DesiredSize.Height / this.RowHeightIncrement); Point p = GetNextAvailabeCell(columnSpan, rowSpan); element.SetValue(ComputedLocationProperty, p); height = element.DesiredSize.Height; top = (p.Y * RowHeightIncrement); if (top + height > totalheight) totalheight = top + height; } return new Size(Math.Min(availableSize.Width, _columnCount * _columnWidth), totalheight); } #endregion Protected Methods  #region Private Methods  private Point GetNextAvailabeCell(int columnSpan, int rowSpan) { int adjustedColumnSpan = columnSpan; if (adjustedColumnSpan > _columnCount) adjustedColumnSpan = _columnCount; int y, x = 0; for (y = 0; y < _maxRowCount; y++) { for (x = 0; x < _columnCount; x++) { if (TestCells(x, y, adjustedColumnSpan, rowSpan)) { SetCells(x, y, adjustedColumnSpan, rowSpan); _rowCount = y + rowSpan; return new Point(x, y); } } } return new Point(0, 1); } private void SetCells(int x, int y, int spanX, int spanY) { if (x + spanX > _columnCount) return; for (int j = y; j < y + spanY; j++) { for (int i = x; i < x + spanX; i++) { cells[i, j] = true; } } } private bool TestCells(int x, int y, int spanX, int spanY) { if (x + spanX > _columnCount) return false; for (int j = y; j < y + spanY; j++) { for (int i = x; i < x + spanX; i++) { if (cells[i, j]) return false; } } return true; } #endregion Private Methods  #region MinColumnWidth /// /// MinColumnWidth Dependency Property /// public static readonly DependencyProperty MinColumnWidthProperty = DependencyProperty.Register("MinColumnWidth", typeof(double), typeof(ColumnWrapPanel), new FrameworkPropertyMetadata((double)100, FrameworkPropertyMetadataOptions.AffectsMeasure, new PropertyChangedCallback(OnMinColumnWidthChanged))); /// /// Gets or sets the MinColumnWidth property. This dependency property /// indicates .... /// public double MinColumnWidth { get { return (double)GetValue(MinColumnWidthProperty); } set { SetValue(MinColumnWidthProperty, value); } } /// /// Handles changes to the MinColumnWidth property. /// private static void OnMinColumnWidthChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { ((ColumnWrapPanel)d).OnMinColumnWidthChanged(e); } /// /// Provides derived classes an opportunity to handle changes to the MinColumnWidth property. /// protected virtual void OnMinColumnWidthChanged(DependencyPropertyChangedEventArgs e) { } #endregion #region RowHeightIncrement /// /// RowHeightIncrement Dependency Property /// public static readonly DependencyProperty RowHeightIncrementProperty = DependencyProperty.Register("RowHeightIncrement", typeof(double), typeof(ColumnWrapPanel), new FrameworkPropertyMetadata((double)20, FrameworkPropertyMetadataOptions.AffectsMeasure)); /// /// Gets or sets the RowHeightIncrement property. This dependency property /// indicates .... /// public double RowHeightIncrement { get { return (double)GetValue(RowHeightIncrementProperty); } set { SetValue(RowHeightIncrementProperty, value); } } #endregion #region ColumnSpan /// /// ColumnSpan Attached Dependency Property /// public static readonly DependencyProperty ColumnSpanProperty = DependencyProperty.RegisterAttached("ColumnSpan", typeof(int), typeof(ColumnWrapPanel), new FrameworkPropertyMetadata((int)1, FrameworkPropertyMetadataOptions.AffectsParentMeasure)); /// /// Gets the ColumnSpan property. This dependency property /// indicates .... /// public static int GetColumnSpan(DependencyObject d) { return (int)d.GetValue(ColumnSpanProperty); } /// /// Sets the ColumnSpan property. This dependency property /// indicates .... /// public static void SetColumnSpan(DependencyObject d, int value) { d.SetValue(ColumnSpanProperty, value); } #endregion #region ComputedLocation /// /// ComputedLocation Attached Dependency Property /// private static readonly DependencyProperty ComputedLocationProperty = DependencyProperty.RegisterAttached("ComputedLocation", typeof(Point), typeof(ColumnWrapPanel), new FrameworkPropertyMetadata((Point)new Point(), FrameworkPropertyMetadataOptions.AffectsParentMeasure)); /// /// Gets the ComputedLocation property. This dependency property /// indicates .... /// private static Point GetComputedLocation(DependencyObject d) { return (Point)d.GetValue(ComputedLocationProperty); } /// /// Sets the ComputedLocation property. This dependency property /// indicates .... /// private static void SetComputedLocation(DependencyObject d, Point value) { d.SetValue(ComputedLocationProperty, value); } #endregion } }