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
}
}