Monday, November 10, 2008

Tutor.com Classroom How-To #6: creating a horizontal WrapPanel that is transform-aware and exposes row and column counts

These How-To tips are taken from The Tutor.com Classroom: Architecture and techniques using Silverlight, WPF, and the Microsoft .NET Framework.

You can check out the Silverlight Tutor.com Classroom in "practice" mode, although the real experience is with a live tutor on the other side!

Begin by deriving from Panel, which is the base class for container controls, and override the MeasureOverride() and ArrangeOverride() functions. These functions are called iteratively as the layout engine determines how to distribute available visual space. Have them call a custom function that takes either the available size (from the measure pass) or the final size (from the arrange pass), and a ShouldArrange parameter to specify which pass is processing, and returns a Size.


private Size measureAndOptionallyArrangeItems(Size size, bool ShouldArrange)
{
Point point = new Point(0, 0);
Size s = new Size(size.Width, 0);

//consider scale transform that might be affecting us
double xfactor = 1;
double yfactor = 1;
if (this.RenderTransform != null)
{
if (this.RenderTransform is ScaleTransform)
{
ScaleTransform st = (ScaleTransform)this.RenderTransform;

xfactor = st.ScaleX;
yfactor = st.ScaleY;
}
}

double largestHeight = 0.0;

this.Rows = 0;
this.Cols = 0;

foreach (UIElement child in Children)
{
if (child.DesiredSize.Height > largestHeight)
largestHeight = child.DesiredSize.Height;

//first row?
if (this.Rows == 0)
this.Rows = 1;

double desiredWidth = child.DesiredSize.Width;

//does this child cause us to wrap?
if (point.X > 0 && point.X + desiredWidth > size.Width * (1 / xfactor))
{
this.Rows++;

s.Height += largestHeight * yfactor;

//goto the next line
point.X = 0;
point.Y += largestHeight;
largestHeight = child.DesiredSize.Height;

if (ShouldArrange)
child.Arrange(new Rect(point, new Point(point.X + desiredWidth, point.Y + child.DesiredSize.Height)));

//set our current location in this new line
point.X = desiredWidth;
}
else
{
if (ShouldArrange)
child.Arrange(new Rect(point, new Point(point.X + desiredWidth, point.Y + child.DesiredSize.Height)));

point.X = point.X + desiredWidth;

//if we're doing first row, set columns
if (this.Rows == 1)
this.Cols++;
}
}

s.Height += largestHeight * yfactor;

return s;
}

No comments: