在Silverlight 2.0 RTM里好像还没有类似WPF里的UniformGrid控件,所以在使用时稍有不便。在网上找到一个老外已经写好的在Silverlight下实现UniformGrid的class,直接继承自Panel,堪称经典,值得学习,故纪录于此。完整示例代码可在这里下载。
![](/Images/OutliningIndicators/ContractedBlock.gif)
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
// (c) Copyright Microsoft Corporation.
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved.
using System;
using System.Windows;
using System.Windows.Controls;
namespace System.Windows.Controls.Primitives
{
/// <summary>
/// A UniformGrid implementation for Silverlight that arranges children in a
/// grid with all equal cell sizes.
/// </summary>
public class UniformGrid : Panel
{
/// <summary>
/// Gets or sets the computed row value.
/// </summary>
private int ComputedRows { get; set; }
/// <summary>
/// Gets or sets the computed column value.
/// </summary>
private int ComputedColumns { get; set; }
/// <summary>
/// Initializes a new instance of UniformGrid.
/// </summary>
public UniformGrid()
{
}
/// <summary>
/// Gets or sets the number of first columns to leave blank.
/// </summary>
public int FirstColumn
{
get { return (int)GetValue(FirstColumnProperty); }
set { SetValue(FirstColumnProperty, value); }
}
/// <summary>
/// The FirstColumnProperty dependency property.
/// </summary>
public static readonly DependencyProperty FirstColumnProperty =
DependencyProperty.Register(
"FirstColumn",
typeof(int),
typeof(UniformGrid),
new PropertyMetadata(0, OnIntegerDependencyPropertyChanged));
/// <summary>
/// Gets or sets the number of columns in the grid. A value of zero
/// indicates that the count should be dynamically computed based on the
/// number of rows and the number of non-collapsed children in the grid.
/// </summary>
public int Columns
{
get { return (int)GetValue(ColumnsProperty); }
set { SetValue(ColumnsProperty, value); }
}
/// <summary>
/// DependencyProperty for the Columns property.
/// </summary>
public static readonly DependencyProperty ColumnsProperty =
DependencyProperty.Register(
"Columns",
typeof(int),
typeof(UniformGrid),
new PropertyMetadata(0, OnIntegerDependencyPropertyChanged));
/// <summary>
/// Validate the new property value and silently revert if the new value
/// is not appropriate. Used in place of WPF value coercian by the
/// dependency properties in UniformGrid.
/// </summary>
/// <param name="o">The dependency object.</param>
/// <param name="e">The dependency property.</param>
private static void OnIntegerDependencyPropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
// Silently coerce the value back to >= 0 if negative.
if (!(e.NewValue is int) || (int)e.NewValue < 0)
{
o.SetValue(e.Property, e.OldValue);
}
}
/// <summary>
/// Gets or sets the number of rows in the grid. A value of zero
/// indicates that the row count should be dynamically computed based on
/// the number of columns and the number of non-collapsed children in
/// the grid.
/// </summary>
public int Rows
{
get { return (int)GetValue(RowsProperty); }
set { SetValue(RowsProperty, value); }
}
/// <summary>
/// The Rows DependencyProperty.
/// </summary>
public static readonly DependencyProperty RowsProperty =
DependencyProperty.Register(
"Rows",
typeof(int),
typeof(UniformGrid),
new PropertyMetadata(0, OnIntegerDependencyPropertyChanged));
/// <summary>
/// Compute the desired size of the UniformGrid by measuring all of the
/// children with a constraint equal to a cell's portion of the given
/// constraint. The maximum child width and maximum child height are
/// tracked, and then the desired size is computed by multiplying these
/// maximums by the row and column count.
/// </summary>
/// <param name="constraint">The size constraint.</param>
/// <returns>Returns the desired size.</returns>
protected override Size MeasureOverride(Size constraint)
{
UpdateComputedValues();
Size childConstraint = new Size(constraint.Width / ComputedColumns, constraint.Height / ComputedRows);
double maxChildDesiredWidth = 0.0;
double maxChildDesiredHeight = 0.0;
// Measure each child, keeping track of max desired width & height.
for (int i = 0, count = Children.Count; i < count; ++i)
{
UIElement child = Children[i];
child.Measure(childConstraint);
Size childDesiredSize = child.DesiredSize;
if (maxChildDesiredWidth < childDesiredSize.Width)
{
maxChildDesiredWidth = childDesiredSize.Width;
}
if (maxChildDesiredHeight < childDesiredSize.Height)
{
maxChildDesiredHeight = childDesiredSize.Height;
}
}
return new Size((maxChildDesiredWidth * ComputedColumns), (maxChildDesiredHeight * ComputedRows));
}
/// <summary>
/// Arrange the children of the UniformGrid by distributing space evenly
/// among the children, making each child the size equal to a cell
/// portion of the arrangeSize parameter.
/// </summary>
/// <param name="arrangeSize">The arrange size.</param>
/// <returns>Returns the updated Size.</returns>
protected override Size ArrangeOverride(Size arrangeSize)
{
Rect childBounds = new Rect(0, 0, arrangeSize.Width / ComputedColumns, arrangeSize.Height / ComputedRows);
double xStep = childBounds.Width;
double xBound = arrangeSize.Width - 1.0;
childBounds.X += childBounds.Width * FirstColumn;
// Arrange and Position each child to the same cell size
foreach (UIElement child in Children)
{
child.Arrange(childBounds);
if (child.Visibility != Visibility.Collapsed)
{
childBounds.X += xStep;
if (childBounds.X >= xBound)
{
childBounds.Y += childBounds.Height;
childBounds.X = 0;
}
}
}
return arrangeSize;
}
/// <summary>
/// If the Rows or Columns values are set to 0, dynamically compute the
/// values based on the actual number of non-collapsed children.
/// </summary>
/// <remarks>
/// In the case when both Rows and Columns are set to 0, the Rows and
/// Columns will be equal, laying out a square grid.
/// </remarks>
private void UpdateComputedValues()
{
ComputedColumns = Columns;
ComputedRows = Rows;
// Reset the first column. This is the same logic performed by WPF.
if (FirstColumn >= ComputedColumns)
{
FirstColumn = 0;
}
if ((ComputedRows == 0) || (ComputedColumns == 0))
{
int nonCollapsedCount = 0;
for (int i = 0, count = Children.Count; i < count; ++i)
{
UIElement child = Children[i];
if (child.Visibility != Visibility.Collapsed)
{
nonCollapsedCount++;
}
}
if (nonCollapsedCount == 0)
{
nonCollapsedCount = 1;
}
if (ComputedRows == 0)
{
if (ComputedColumns > 0)
{
ComputedRows = (nonCollapsedCount + FirstColumn + (ComputedColumns - 1)) / ComputedColumns;
}
else
{
ComputedRows = (int)Math.Sqrt(nonCollapsedCount);
if ((ComputedRows * ComputedRows) < nonCollapsedCount)
{
ComputedRows++;
}
ComputedColumns = ComputedRows;
}
}
else if (ComputedColumns == 0)
{
ComputedColumns = (nonCollapsedCount + (ComputedRows - 1)) / ComputedRows;
}
}
}
}
}