/************************************************************************************* Extended WPF Toolkit Copyright (C) 2007-2013 Xceed Software Inc. This program is provided to you under the terms of the Microsoft Public License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license For more features, controls, and fast professional support, pick up the Plus Edition at http://xceed.com/wpf_toolkit Stay informed: follow @datagrid on Twitter or Like http://facebook.com/datagrids ***********************************************************************************/ using System; using System.Collections.ObjectModel; using System.ComponentModel; using System.Windows; using System.Windows.Controls; using Xceed.Wpf.Toolkit.Core.Utilities; namespace Xceed.Wpf.Toolkit.Panels { public class WrapPanel : AnimationPanel { #region Orientation Property public static readonly DependencyProperty OrientationProperty = StackPanel.OrientationProperty.AddOwner( typeof( WrapPanel ), new FrameworkPropertyMetadata( Orientation.Horizontal, new PropertyChangedCallback( WrapPanel.OnOrientationChanged ) ) ); public Orientation Orientation { get { return _orientation; } set { base.SetValue( WrapPanel.OrientationProperty, value ); } } private static void OnOrientationChanged( DependencyObject d, DependencyPropertyChangedEventArgs e ) { WrapPanel panel = ( WrapPanel )d; panel._orientation = ( Orientation )e.NewValue; panel.InvalidateMeasure(); } private Orientation _orientation; #endregion #region ItemWidth Property public static readonly DependencyProperty ItemWidthProperty = DependencyProperty.Register( "ItemWidth", typeof( double ), typeof( WrapPanel ), new FrameworkPropertyMetadata( double.NaN, new PropertyChangedCallback( WrapPanel.OnInvalidateMeasure ) ), new ValidateValueCallback( WrapPanel.IsWidthHeightValid ) ); [TypeConverter( typeof( LengthConverter ) )] public double ItemWidth { get { return ( double )base.GetValue( WrapPanel.ItemWidthProperty ); } set { base.SetValue( WrapPanel.ItemWidthProperty, value ); } } #endregion #region ItemHeight Property public static readonly DependencyProperty ItemHeightProperty = DependencyProperty.Register( "ItemHeight", typeof( double ), typeof( WrapPanel ), new FrameworkPropertyMetadata( double.NaN, new PropertyChangedCallback( WrapPanel.OnInvalidateMeasure ) ), new ValidateValueCallback( WrapPanel.IsWidthHeightValid ) ); [TypeConverter( typeof( LengthConverter ) )] public double ItemHeight { get { return ( double )base.GetValue( WrapPanel.ItemHeightProperty ); } set { base.SetValue( WrapPanel.ItemHeightProperty, value ); } } #endregion #region IsChildOrderReversed Property public static readonly DependencyProperty IsStackReversedProperty = DependencyProperty.Register( "IsChildOrderReversed", typeof( bool ), typeof( WrapPanel ), new FrameworkPropertyMetadata( false, new PropertyChangedCallback( WrapPanel.OnInvalidateMeasure ) ) ); public bool IsChildOrderReversed { get { return ( bool )this.GetValue( WrapPanel.IsStackReversedProperty ); } set { this.SetValue( WrapPanel.IsStackReversedProperty, value ); } } #endregion protected override Size MeasureChildrenOverride( UIElementCollection children, Size constraint ) { double desiredExtent = 0; double desiredStack = 0; bool isHorizontal = ( this.Orientation == Orientation.Horizontal ); double constraintExtent = ( isHorizontal ? constraint.Width : constraint.Height ); double itemWidth = ItemWidth; double itemHeight = ItemHeight; double itemExtent = ( isHorizontal ? itemWidth : itemHeight ); bool hasExplicitItemWidth = !double.IsNaN( itemWidth ); bool hasExplicitItemHeight = !double.IsNaN( itemHeight ); bool useItemExtent = ( isHorizontal ? hasExplicitItemWidth : hasExplicitItemHeight ); double lineExtent = 0; double lineStack = 0; Size childConstraint = new Size( ( hasExplicitItemWidth ? itemWidth : constraint.Width ), ( hasExplicitItemHeight ? itemHeight : constraint.Height ) ); bool isReversed = this.IsChildOrderReversed; int from = isReversed ? children.Count - 1 : 0; int to = isReversed ? 0 : children.Count - 1; int step = isReversed ? -1 : 1; for( int i = from, pass = 0; pass < children.Count; i += step, pass++ ) { UIElement child = children[ i ] as UIElement; child.Measure( childConstraint ); double childExtent = isHorizontal ? ( hasExplicitItemWidth ? itemWidth : child.DesiredSize.Width ) : ( hasExplicitItemHeight ? itemHeight : child.DesiredSize.Height ); double childStack = isHorizontal ? ( hasExplicitItemHeight ? itemHeight : child.DesiredSize.Height ) : ( hasExplicitItemWidth ? itemWidth : child.DesiredSize.Width ); if( lineExtent + childExtent > constraintExtent ) { desiredExtent = Math.Max( lineExtent, desiredExtent ); desiredStack += lineStack; lineExtent = childExtent; lineStack = childStack; if( childExtent > constraintExtent ) { desiredExtent = Math.Max( childExtent, desiredExtent ); desiredStack += childStack; lineExtent = 0; lineStack = 0; } } else { lineExtent += childExtent; lineStack = Math.Max( childStack, lineStack ); } } desiredExtent = Math.Max( lineExtent, desiredExtent ); desiredStack += lineStack; return isHorizontal ? new Size( desiredExtent, desiredStack ) : new Size( desiredStack, desiredExtent ); } protected override Size ArrangeChildrenOverride( UIElementCollection children, Size finalSize ) { bool isHorizontal = ( this.Orientation == Orientation.Horizontal ); double finalExtent = ( isHorizontal ? finalSize.Width : finalSize.Height ); double itemWidth = this.ItemWidth; double itemHeight = this.ItemHeight; double itemExtent = ( isHorizontal ? itemWidth : itemHeight ); bool hasExplicitItemWidth = !double.IsNaN( itemWidth ); bool hasExplicitItemHeight = !double.IsNaN( itemHeight ); bool useItemExtent = ( isHorizontal ? hasExplicitItemWidth : hasExplicitItemHeight ); double lineExtent = 0; double lineStack = 0; double lineStackSum = 0; int from = this.IsChildOrderReversed ? children.Count - 1 : 0; int to = this.IsChildOrderReversed ? 0 : children.Count - 1; int step = this.IsChildOrderReversed ? -1 : 1; Collection childrenInLine = new Collection(); for( int i = from, pass = 0; pass < children.Count; i += step, pass++ ) { UIElement child = children[ i ] as UIElement; double childExtent = isHorizontal ? ( hasExplicitItemWidth ? itemWidth : child.DesiredSize.Width ) : ( hasExplicitItemHeight ? itemHeight : child.DesiredSize.Height ); double childStack = isHorizontal ? ( hasExplicitItemHeight ? itemHeight : child.DesiredSize.Height ) : ( hasExplicitItemWidth ? itemWidth : child.DesiredSize.Width ); if( lineExtent + childExtent > finalExtent ) { this.ArrangeLineOfChildren( childrenInLine, isHorizontal, lineStack, lineStackSum, itemExtent, useItemExtent ); lineStackSum += lineStack; lineExtent = childExtent; if( childExtent > finalExtent ) { childrenInLine.Add( child ); this.ArrangeLineOfChildren( childrenInLine, isHorizontal, childStack, lineStackSum, itemExtent, useItemExtent ); lineStackSum += childStack; lineExtent = 0; } childrenInLine.Add( child ); } else { childrenInLine.Add( child ); lineExtent += childExtent; lineStack = Math.Max( childStack, lineStack ); } } if( childrenInLine.Count > 0 ) { this.ArrangeLineOfChildren( childrenInLine, isHorizontal, lineStack, lineStackSum, itemExtent, useItemExtent ); } return finalSize; } private void ArrangeLineOfChildren( Collection children, bool isHorizontal, double lineStack, double lineStackSum, double itemExtent, bool useItemExtent ) { double extent = 0; foreach( UIElement child in children ) { double childExtent = ( isHorizontal ? child.DesiredSize.Width : child.DesiredSize.Height ); double elementExtent = ( useItemExtent ? itemExtent : childExtent ); this.ArrangeChild( child, isHorizontal ? new Rect( extent, lineStackSum, elementExtent, lineStack ) : new Rect( lineStackSum, extent, lineStack, elementExtent ) ); extent += elementExtent; } children.Clear(); } private static void OnInvalidateMeasure( DependencyObject d, DependencyPropertyChangedEventArgs e ) { ( ( AnimationPanel )d ).InvalidateMeasure(); } private static bool IsWidthHeightValid( object value ) { double num = ( double )value; return ( DoubleHelper.IsNaN( num ) || ( ( num >= 0d ) && !double.IsPositiveInfinity( num ) ) ); } } }