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;
		}
Beispiel #4
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 #5
0
 // CTOR
 public GridPanel()
 {
     _actualHeightChangeNotifier = new PropertyChangeNotifier(this, GridPanel.ActualHeightProperty);
     _actualHeightChangeNotifier.ValueChanged += this.ActualHeightChanged;
 }