Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WPF - How can I center all items in a WrapPanel?

I'm using a WrapPanel as the ItemsPanel of an ItemsControl. Right now, the items in the control wrap like this:

|1234567  | |890      | 

I'd like them to wrap like this:

| 1234567 | |   890   | 

Conceptually, the layout process should align each line of items such that it's centered within the WrapPanel's bounds.

Can someone explain how this is possible please?

like image 535
Drew Noakes Avatar asked Apr 30 '09 13:04

Drew Noakes


2 Answers

The built in WrapPanel will not allow you to align its content - only itself. Here is a technique that allows you to set HorizontalContentAlignment:

using System; using System.Windows; using System.Windows.Controls;  public class AlignableWrapPanel : Panel {     public HorizontalAlignment HorizontalContentAlignment     {         get { return (HorizontalAlignment)GetValue(HorizontalContentAlignmentProperty); }         set { SetValue(HorizontalContentAlignmentProperty, value); }     }      public static readonly DependencyProperty HorizontalContentAlignmentProperty =         DependencyProperty.Register("HorizontalContentAlignment", typeof(HorizontalAlignment), typeof(AlignableWrapPanel), new FrameworkPropertyMetadata(HorizontalAlignment.Left, FrameworkPropertyMetadataOptions.AffectsArrange));      protected override Size MeasureOverride(Size constraint)     {         Size curLineSize = new Size();         Size panelSize = new Size();          UIElementCollection children = base.InternalChildren;          for (int i = 0; i < children.Count; i++)         {             UIElement child = children[i] as UIElement;              // Flow passes its own constraint to children             child.Measure(constraint);             Size sz = child.DesiredSize;              if (curLineSize.Width + sz.Width > constraint.Width) //need to switch to another line             {                 panelSize.Width = Math.Max(curLineSize.Width, panelSize.Width);                 panelSize.Height += curLineSize.Height;                 curLineSize = sz;                  if (sz.Width > constraint.Width) // if the element is wider then the constraint - give it a separate line                                     {                     panelSize.Width = Math.Max(sz.Width, panelSize.Width);                     panelSize.Height += sz.Height;                     curLineSize = new Size();                 }             }             else //continue to accumulate a line             {                 curLineSize.Width += sz.Width;                 curLineSize.Height = Math.Max(sz.Height, curLineSize.Height);             }         }          // the last line size, if any need to be added         panelSize.Width = Math.Max(curLineSize.Width, panelSize.Width);         panelSize.Height += curLineSize.Height;          return panelSize;     }      protected override Size ArrangeOverride(Size arrangeBounds)     {         int firstInLine = 0;         Size curLineSize = new Size();         double accumulatedHeight = 0;         UIElementCollection children = this.InternalChildren;          for (int i = 0; i < children.Count; i++)         {             Size sz = children[i].DesiredSize;              if (curLineSize.Width + sz.Width > arrangeBounds.Width) //need to switch to another line             {                 ArrangeLine(accumulatedHeight, curLineSize, arrangeBounds.Width, firstInLine, i);                  accumulatedHeight += curLineSize.Height;                 curLineSize = sz;                  if (sz.Width > arrangeBounds.Width) //the element is wider then the constraint - give it a separate line                                     {                     ArrangeLine(accumulatedHeight, sz, arrangeBounds.Width, i, ++i);                     accumulatedHeight += sz.Height;                     curLineSize = new Size();                 }                 firstInLine = i;             }             else //continue to accumulate a line             {                 curLineSize.Width += sz.Width;                 curLineSize.Height = Math.Max(sz.Height, curLineSize.Height);             }         }          if (firstInLine < children.Count)             ArrangeLine(accumulatedHeight, curLineSize, arrangeBounds.Width, firstInLine, children.Count);          return arrangeBounds;     }      private void ArrangeLine(double y, Size lineSize, double boundsWidth, int start, int end)     {         double x = 0;         if (this.HorizontalContentAlignment == HorizontalAlignment.Center)         {             x = (boundsWidth - lineSize.Width) / 2;         }         else if (this.HorizontalContentAlignment == HorizontalAlignment.Right)         {             x = (boundsWidth - lineSize.Width);         }          UIElementCollection children = InternalChildren;         for (int i = start; i < end; i++)         {             UIElement child = children[i];             child.Arrange(new Rect(x, y, child.DesiredSize.Width, lineSize.Height));             x += child.DesiredSize.Width;         }     } } 
like image 60
DTig Avatar answered Sep 18 '22 16:09

DTig


Unfortunately the stock WrapPanel will not do what you're asking for, because its intention is to fill all available space horizontally (or vertically) before wrapping. You will probably have to design your own WrapPanel to handle this case. You could start with this sample that shows how to create your own WrapPanel.

like image 38
user7116 Avatar answered Sep 19 '22 16:09

user7116