chrisroth

chrisroth


  • Name: Chris Rothery
  • Favorite Languages: c#, xaml, x++
  • Website: [not set]
  • Location: UK
  • About Me: [not set]

Recent Comments

  • WPF Tutorial - Creating A Custom Panel Control
    05/08/2009 - 04:44

    Cool, I'm glad you're interested in my code (I've registered now so not anonymous anymore!).

    It could do with some tidying but it seems to be working well at the moment. Most of the mess comes from working out the rows/columns.

    If we're willing to accept more restrictions (which I think I can for my application), you could fix the item size which would allow you to make decisions in the measure phase about whether the offered size has changed enough to warrant recalculating the number of rows/columns (although making that decision would probably cancel out any gains!).

    Hope this helps someone and if you can assist improving it I'm all ears!

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;

    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Media;
    using System.Windows.Media.Animation;

    namespace MyNamespace
    {    
        public class AnimatedGridPanel : Panel
        {
            // grid size taken from largest child
            private static double gridItemWidth;        
            private static double gridItemHeight;        

            private static double gridItemMargin = 3;
            public Double GridItemMargin
            {
                set
                {
                    gridItemMargin = value;
                }
                get { return gridItemMargin; }
            }

            private int nrRows = 0;
            private int nrCols = 0;
            private Boolean nrRowsColsChangedRecently = false;

            static AnimatedGridPanel()
            {            
            }

            protected override Size MeasureOverride(Size availableSize)
            {
                Size resultSize = new Size(0, 0);
                Size maxCellSize = new Size(0, 0);

                // important step, children dont like not being measured
                foreach (UIElement child in Children)
                {
                    child.Measure(availableSize);
                    maxCellSize.Width = Math.Max(resultSize.Width, child.DesiredSize.Width);
                    maxCellSize.Height = Math.Max(resultSize.Height, child.DesiredSize.Height);                
                }

                gridItemWidth = maxCellSize.Width;
                gridItemHeight = maxCellSize.Height;            

                int newNrCols = 0;
                if (Double.IsPositiveInfinity(availableSize.Width))
                {
                    newNrCols = Children.Count;
                    resultSize.Width = Children.Count * (maxCellSize.Width + GridItemMargin);
                }
                else
                {
                    newNrCols = (int)Math.Ceiling(availableSize.Width / (maxCellSize.Width + GridItemMargin));
                    resultSize.Width = availableSize.Width;
                }

                if (newNrCols != nrCols)
                {
                    nrCols = newNrCols;
                    nrRowsColsChangedRecently = true;
                }

                int newNrRows = 0;
                if (Double.IsPositiveInfinity(availableSize.Height))
                {
                    newNrRows = (int)Math.Ceiling((double)Children.Count / nrCols); // how many do we need
                    resultSize.Height = newNrRows * (maxCellSize.Height + GridItemMargin);
                }
                else
                {
                    newNrRows = (int)Math.Ceiling(availableSize.Height / (maxCellSize.Height + GridItemMargin)); // how many could we fit
                    resultSize.Height = availableSize.Height;
                }

                if (newNrRows != nrRows)
                {
                    nrRows = newNrRows;
                    nrRowsColsChangedRecently = true;
                }

                return resultSize;
            }      

            protected override Size ArrangeOverride(Size finalSize)
            {
                // update children only if the
                // number of columns has changed for this item and animates those children that
                // need to move
                int row = 0;
                int col = 0;
                double x = 0;
                double y = 0;
                TranslateTransform trans = null;

                // I didn't think not arranging a child would work but it seems to
                if (!nrRowsColsChangedRecently) return finalSize;
                int n = 0;

                nrRowsColsChangedRecently = false;

                foreach (UIElement child in Children)
                {
                    if (child is ContentPresenter) continue;

                    trans = child.RenderTransform as TranslateTransform;
                    if (trans == null)
                    {
                        child.RenderTransformOrigin = new Point(0, 0);
                        trans = new TranslateTransform();
                        child.RenderTransform = trans;
                    }
                   
                    x = ((gridItemWidth + gridItemMargin) * col) + gridItemMargin;
                    y = ((gridItemHeight + gridItemMargin) * row) + gridItemMargin;
                   
                    child.Arrange(new Rect(0, 0, child.DesiredSize.Width, child.DesiredSize.Height));

                    DoubleAnimation xda = new DoubleAnimation(x, TimeSpan.FromMilliseconds(250));
                    DoubleAnimation yda = new DoubleAnimation(y, TimeSpan.FromMilliseconds(250));
                   
                    trans.BeginAnimation(TranslateTransform.XProperty,
                            xda,
                            HandoffBehavior.Compose);

                    trans.BeginAnimation(TranslateTransform.YProperty,
                            yda,
                            HandoffBehavior.Compose);


                    col++;
                    if (col >= nrCols - 1)
                    {
                        col = 0;
                        row++;
                    }
                    n++;
                }

                return finalSize;
            }        
        }
    }