GARbro-mirror/GridLayout.cs

108 lines
4.7 KiB
C#
Raw Permalink Normal View History

// http://daniel-albuschat.blogspot.ru/2011/07/gridlayout-for-wpf-escape-margin-hell.html
//
using System.Windows;
using System.Windows.Controls;
namespace GridLayoutTest
{
// The GridLayout is a special Panel that can be used exactly like the Grid Panel, except that it
// defines a new property ChildMargin. ChildMargin's left, top, right and bottom margins will be applied
// to all children in a way that the children will have a vertical space of ChildMargin.Top+ChildMargin.Bottom
// and a horizontal space of ChildMargin.Left+ChildMargin.Right between them.
// However, there is no margin for the borders of the internal widget, so that the GridLayout itself can be
// aligned to another element without a margin.
// It's best to have a look at TestWindow, which effectively tests all possible alignments of children.
public class GridLayout : Grid
{
public static readonly DependencyProperty ChildMarginProperty = DependencyProperty.Register(
"ChildMargin",
typeof(Thickness),
typeof(GridLayout),
new FrameworkPropertyMetadata(new Thickness (5))
{
AffectsArrange = true,
AffectsMeasure = true
});
// The child margin defines a margin that will be automatically applied to all children of this Grid.
// However, the children at the edges will have the respective margins remove. E.g. the leftmost children will have
// a Margin.Left of 0 and the children in the first row will have a Margin.Top of 0.
// The margins that are not set to 0 are set to half the ChildMargin's value, since it's neighbour will also apply it,
// effectively doubling it.
public Thickness ChildMargin
{
get { return (Thickness)GetValue(ChildMarginProperty); }
set
{
SetValue(ChildMarginProperty, value);
UpdateChildMargins();
}
}
// UpdateChildMargin first finds out what's the rightmost column and bottom row and then applies
// the correct margins to all children.
public void UpdateChildMargins()
{
int maxColumn = 0;
int maxRow = 0;
foreach (UIElement element in InternalChildren)
{
int row = GetRow(element);
int rowSpan = GetRowSpan(element);
int column = GetColumn(element);
int columnSpan = GetColumnSpan(element);
if (row + rowSpan > maxRow)
maxRow = row + rowSpan;
if (column + columnSpan> maxColumn)
maxColumn = column + columnSpan;
}
foreach (UIElement element in InternalChildren)
{
FrameworkElement fe = element as FrameworkElement;
if (null != fe)
{
int row = GetRow(fe);
int rowSpan = GetRowSpan(fe);
int column = GetColumn(fe);
int columnSpan = GetColumnSpan(fe);
double factorLeft = 0.5;
double factorTop = 0.5;
double factorRight = 0.5;
double factorBottom = 0.5;
// Top row - no top margin
if (row == 0)
factorTop = 0;
// Bottom row - no bottom margin
if (row + rowSpan >= maxRow)
factorBottom = 0;
// Leftmost column = no left margin
if (column == 0)
factorLeft = 0;
// Rightmost column - no right margin
if (column + columnSpan >= maxColumn)
factorRight = 0;
fe.Margin = new Thickness (ChildMargin.Left * factorLeft,
ChildMargin.Top * factorTop,
ChildMargin.Right * factorRight,
ChildMargin.Bottom * factorBottom);
}
}
}
// We change all children's margins in MeasureOverride, since this is called right before
// the layouting takes place. I was first skeptical to do this here, because I thought changing
// the margin will trigger a LayoutUpdate, which in turn would lead to an endless recursion,
// but apparantly WPF takes care of this.
protected override Size MeasureOverride(Size availableSize)
{
UpdateChildMargins();
return base.MeasureOverride(availableSize);
}
}
}