Beispiel #1
0
        private static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            PropertyChangeNotifier notifier = (PropertyChangeNotifier)d;

            if (null != notifier.ValueChanged)
            {
                notifier.ValueChanged(notifier, EventArgs.Empty);
            }
        }
Beispiel #2
0
        // the meat and potatos...lays out the grid
        private void ArrangeChildren()
        {
            // the layout strategy is:
            //	1. Fill all available rows
            //	2. Reduce the amount of free space at the end of each row
            //	3. Fill the top rows completely

            // this can be called before the control has been instantiated so _numberOfRows could be zero
            if (_numberOfRows == 0)
            {
                return;
            }
#if DEBUG
            // pedantic checking of performance...
            System.Diagnostics.Stopwatch st = new System.Diagnostics.Stopwatch();
            st.Start();
#endif

            // cache these for performance
            double    itemWidth  = this.ItemWidth;
            double    itemHeight = this.ItemHeight;
            Thickness itemMargin = this.ItemMargin;

            int itemsCount = this.Children.Count;

            // no children, no layout
            if (itemsCount == 0)
            {
                return;
            }

            // cache for the children
            List <RowLayoutItem> items = new List <RowLayoutItem>();

            // do we need to attach a notifier to the children?
            bool createItemWidthSpanChangeNotifier = false;
            if (this.TrackItemWidthSpanChanges && _itemWidthSpanChangeNotifier == null)
            {
                createItemWidthSpanChangeNotifier = true;
                _itemWidthSpanChangeNotifier      = new PropertyChangeNotifier[itemsCount];
            }

            int totalWidth = 0;
            int maxWidth   = 0;
            int calculatedItemWidth;

            // first pass calculate the total width (in item units) and find the maximum item width
            // as the total width cannot be less than this
            int index = 0;
            foreach (FrameworkElement item in this.Children)
            {
                items.Add(new RowLayoutItem()
                {
                    ItemWidth = calculatedItemWidth = GridPanel.GetItemWidthSpan(item), Element = item
                });
                if (calculatedItemWidth > maxWidth)
                {
                    maxWidth = calculatedItemWidth;
                }
                totalWidth += calculatedItemWidth;

                if (createItemWidthSpanChangeNotifier)
                {
                    PropertyChangeNotifier pcn = new PropertyChangeNotifier(item, GridPanel.ItemWidthSpanProperty);
                    pcn.ValueChanged += this.ItemWidthSpanChanged;
                    _itemWidthSpanChangeNotifier[index++] = pcn;
                }
            }

            // second pass layout the items with a width of totalWidth / _numberOfRows
            // we also want to reduce the 'jaggedness' of the right hand side and so
            // an optimal layout is defined where the space on the right hand side is a minimum
            // where all rows have been laid out (otherwise it'll always lay it out in a single line!)

            RowLayout[] optimalRows  = null;
            int         minimumSpace = int.MaxValue;
            bool        allRowsFilled;

            // width of one row
            int rowWidth = Math.Min(maxWidth, (totalWidth / _numberOfRows) + (totalWidth % _numberOfRows > 0 ? 1 : 0));

            // temporary variables held outside the loop to reuce stack allocations
            RowLayout[]   rows;
            RowLayout     rowItem;
            RowLayoutItem rowLayoutItem;

#if DEBUG
            int numberOfIterations = 0;
#endif
            maxWidth = 0;
            int space = 0;

            // do this until all the elements have been laid out
            do
            {
                // elements in each row
                rows = new RowLayout[_numberOfRows];

                // index into elements array
                index = 0;

                // check to see all rows in the layout are filled
                allRowsFilled = false;

                // lay out the items
                for (int row = 0; row < _numberOfRows; row++)
                {
                    // allocate the row layout
                    rows[row] = rowItem = new RowLayout();

                    // allocate elements to the row layout until they won't fit into the space or we run out of items
                    while (index < itemsCount && rowItem.TotalRowWidth < rowWidth)
                    {
                        // is the item to be added greater than the allowed row width
                        if (rowItem.TotalRowWidth + (calculatedItemWidth = (rowLayoutItem = items[index]).ItemWidth) > totalWidth)
                        {
                            break;
                        }

                        // if we're on the last row then all rows have been filled
                        if (!allRowsFilled && row == _numberOfRows - 1)
                        {
                            allRowsFilled = true;
                        }

                        // we can fit the item so add to the row list and increment the width
                        rowItem.RowItems.Add(rowLayoutItem);
                        index++;

                        // keep a copy of the maximum width
                        if ((rowItem.TotalRowWidth += calculatedItemWidth) > maxWidth)
                        {
                            maxWidth = rowItem.TotalRowWidth;
                        }
                    }
                }
#if DEBUG
                numberOfIterations++;
#endif
                // if we couldn't lay out all the elements, increment the row width by 1 and try again.
                if (index < itemsCount)
                {
                    rowWidth++;
                }
                // if all rows filled, check for a minimum of space
                // but also weigh fill from the top so that it'll look neat by multiplying by the inverse row position squared
                else if (allRowsFilled)
                {
                    // check the free space
                    space = 0;
                    // calculate the free space and add the weighting
                    for (int i = 0; i < _numberOfRows; i++)
                    {
                        space += (maxWidth - rows[i].TotalRowWidth) * (_numberOfRows - i) * (_numberOfRows - i);
                    }

                    // if the row has less free space, cache it
                    if (space < minimumSpace)
                    {
                        minimumSpace = space;
                        optimalRows  = rows;
                    }
                    // increment the row width to test the next solution
                    rowWidth++;
                }
            }
            // keep going until we have a blank last row (i.e. fails the criterion that all rows are filled)
            while (allRowsFilled);

            // it IS possible not to find a solution (may need to fix this) but unlikely for ItemWidthSpan = 1 or 2
            if (optimalRows == null)
            {
                return;
            }

            // get the actual row width (not the guesstimate)
            rowWidth = 0;
            foreach (RowLayout r in optimalRows)
            {
                if (r.TotalRowWidth > rowWidth)
                {
                    rowWidth = r.TotalRowWidth;
                }
            }

            // set the Width of the canvas (obviously, don't set the height!!)
            if (rowWidth > 0)
            {
                this.Width = (rowWidth * itemWidth) + ((itemMargin.Left + itemMargin.Right) * (rowWidth));
            }
            else
            {
                this.Width = 0;
            }
            // layout the items
            double           heightOffset = itemMargin.Top, widthOffset;
            FrameworkElement elem;
            foreach (RowLayout row in optimalRows)
            {
                widthOffset = itemMargin.Left;
                foreach (RowLayoutItem item in row.RowItems)
                {
                    Canvas.SetLeft(elem = item.Element, widthOffset);
                    Canvas.SetTop(elem, heightOffset);

                    elem.Width   = ((calculatedItemWidth = item.ItemWidth) * itemWidth) + ((itemMargin.Left + itemMargin.Right) * (calculatedItemWidth - 1));
                    widthOffset += (calculatedItemWidth * itemWidth) + ((itemMargin.Left + itemMargin.Right) * calculatedItemWidth);
                    elem.Height  = itemHeight;
                }
                heightOffset += itemHeight + itemMargin.Top + itemMargin.Bottom;
            }
#if DEBUG
            st.Stop();
            System.Diagnostics.Debug.WriteLine("GridPanel.ArrangeChildren: Number of items=" + itemsCount + ", Number of iterations=" + numberOfIterations + ", Time=" + st.Elapsed.TotalMilliseconds + "ms");
#endif
        }
Beispiel #3
0
 // CTOR
 public GridPanel()
 {
     _actualHeightChangeNotifier = new PropertyChangeNotifier(this, GridPanel.ActualHeightProperty);
     _actualHeightChangeNotifier.ValueChanged += this.ActualHeightChanged;
 }