title: Listbox - Size to content until Max number of rows is met
body: <p>I'd like to have a ListBox that sizes to its content, until some MaxRow property is met. So, if the MaxRow value was 3, it would behave in the following manner.</p>
  
  <p>Items.Count == 0 -> SizeToContent</p>
  
  <p>Items.Count == 1 -> SizeToContent</p>
  
  <p>Items.Count == 2 -> SizeToContent </p>
  
  <p>Items.Count == 3 -> SizeToContent</p>
  
  <p>Items.Count == 4 -> limit height to 3 rows and enable scrollbar</p>
  
  <p>Items.Count == 5 -> limit height to 3 rows and enable scrollbar
  etc etc</p>
  
  <p>I thought the correct way to do this would be to use a custom Panel (as seen below), but this doesn't seem to work.</p>
  
  <p>How could I achieve this?</p>
  
  <pre><code>&lt;ListBox ItemsSource="{Binding Items}"
           HorizontalContentAlignment="Stretch"&gt;
          &lt;ListBox.Template&gt;
              &lt;ControlTemplate&gt;
                  &lt;ScrollViewer VerticalScrollBarVisibility="Auto"&gt;
                      &lt;l:LimitingStackPanel VerticalAlignment="Top"
                                            IsItemsHost="True"/&gt;
                  &lt;/ScrollViewer&gt;
              &lt;/ControlTemplate&gt;
          &lt;/ListBox.Template&gt;
      &lt;/ListBox&gt;
  
  
  public class LimitingStackPanel : Panel
  {
      protected override Size MeasureOverride(Size availableSize)
      {
          Size measuredSize = new Size(0, 0);
          int count = 0;
          foreach(UIElement item in InternalChildren)
          {
              item.Measure(availableSize);
              measuredSize.Width = Math.Max(measuredSize.Width, item.DesiredSize.Width);
  
              if(++count &lt;= 4)
              {
                  measuredSize.Height += item.DesiredSize.Height;
              }
          }
  
          return measuredSize;
      }
  
      protected override Size ArrangeOverride(Size finalSize)
      {
          double y = 0;
          foreach (UIElement item in InternalChildren)
          {
              double height = item.DesiredSize.Height;
              item.Arrange(new Rect(0, y, finalSize.Width, height));
              y += height;
          }
  
          return new Size(finalSize.Width, y);
      }
  }
  </code></pre>
  
  <p>Edit : Here is an example of what I'm trying to achieve
  <img src="https://i.stack.imgur.com/L8biB.png" alt="enter image description here"></p>
  
  <p>Edit : The is the solution I used in the end (based on the answer from PushPraj)</p>
  
  <pre><code>&lt;DockPanel&gt;
      &lt;UniformGrid Columns="2"
                   DockPanel.Dock="Top"&gt;
          &lt;Button Content="Add"
                  Click="OnAddClick" /&gt;
          &lt;Button Content="Remove"
                  Click="OnRemoveClick" /&gt;
      &lt;/UniformGrid&gt;
  
      &lt;StackPanel&gt;
          &lt;ListBox l:ListBoxHelper.AutoSizeItemCount="3"
                   ItemsSource="{Binding ItemsA}" /&gt;
          &lt;ListBox l:ListBoxHelper.AutoSizeItemCount="3"
                   ItemsSource="{Binding ItemsB}" /&gt;
          &lt;ListBox l:ListBoxHelper.AutoSizeItemCount="3"
                   ItemsSource="{Binding ItemsC}" /&gt;
          &lt;ListBox l:ListBoxHelper.AutoSizeItemCount="3"
                   ItemsSource="{Binding ItemsD}" /&gt;
          &lt;ListBox l:ListBoxHelper.AutoSizeItemCount="3"
                   ItemsSource="{Binding ItemsE}" /&gt;
          &lt;ListBox l:ListBoxHelper.AutoSizeItemCount="3"
                   ItemsSource="{Binding ItemsF}" /&gt;
      &lt;/StackPanel&gt;
  &lt;/DockPanel&gt;
  
  public class ListBoxHelper : DependencyObject
  {
      public static int GetAutoSizeItemCount(DependencyObject obj)
      {
          return (int)obj.GetValue(AutoSizeItemCountProperty);
      }
  
      public static void SetAutoSizeItemCount(DependencyObject obj, int value)
      {
          obj.SetValue(AutoSizeItemCountProperty, value);
      }
  
      public static readonly DependencyProperty AutoSizeItemCountProperty =
          DependencyProperty.RegisterAttached("AutoSizeItemCount", typeof(int), typeof(ListBoxHelper), new PropertyMetadata(0, OnAutoSizeItemCountChanged));
  
      static void OnAutoSizeItemCountChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
      {
          var listBox = d as ListBox;
  
          // we set this to 0.0 so that we ddon't create any elements
          // before we have had a chance to modify the scrollviewer
          listBox.MaxHeight = 0.0;
  
          listBox.Loaded += OnListBoxLoaded;
      }
  
      static void OnListBoxLoaded(object sender, RoutedEventArgs e)
      {
          var listBox = sender as ListBox;
  
          var sv = Helper.GetChildOfType&lt;ScrollViewer&gt;(listBox);
          if(sv != null)
          {
              // limit the scrollviewer height so that the bare minimum elements are generated
              sv.MaxHeight = 1.0;
  
              var vsp = Helper.GetChildOfType&lt;VirtualizingStackPanel&gt;(listBox);
              if(vsp != null)
              {
                  vsp.SizeChanged += OnVirtualizingStackPanelSizeChanged;
              }
          }
  
          listBox.MaxHeight = double.PositiveInfinity;
      }
  
      static void OnVirtualizingStackPanelSizeChanged(object sender, SizeChangedEventArgs e)
      {
          var vsp = sender as VirtualizingStackPanel;
          var lb = (ListBox)ItemsControl.GetItemsOwner(vsp);
          int maxCount = GetAutoSizeItemCount(lb);
          vsp.ScrollOwner.MaxHeight = vsp.Children.Count == 0 ? 1 : ((FrameworkElement)vsp.Children[0]).ActualHeight * maxCount;
      }
  }
  
  public static class Helper
  {
      public static T GetChildOfType&lt;T&gt;(this DependencyObject depObj) where T : DependencyObject
      {
          if (depObj == null) return null;
  
          for (int i = 0; i &lt; VisualTreeHelper.GetChildrenCount(depObj); i++)
          {
              var child = VisualTreeHelper.GetChild(depObj, i);
              var result = (child as T) ?? GetChildOfType&lt;T&gt;(child);
              if (result != null) return result;
          }
          return null;
      }
  }
  
  public partial class MainWindow : Window
  {
      public ObservableCollection&lt;string&gt; ItemsA { get; private set; }
      public ObservableCollection&lt;string&gt; ItemsB { get; private set; }
      public ObservableCollection&lt;string&gt; ItemsC { get; private set; }
      public ObservableCollection&lt;string&gt; ItemsD { get; private set; }
      public ObservableCollection&lt;string&gt; ItemsE { get; private set; }
      public ObservableCollection&lt;string&gt; ItemsF { get; private set; }
  
      public MainWindow()
      {
          ItemsA = new ObservableCollection&lt;string&gt;(Enumerable.Repeat("Word", 0));
          ItemsB = new ObservableCollection&lt;string&gt;(Enumerable.Repeat("Word", 1));
          ItemsC = new ObservableCollection&lt;string&gt;(Enumerable.Repeat("Word", 2));
          ItemsD = new ObservableCollection&lt;string&gt;(Enumerable.Repeat("Word", 3));
          ItemsE = new ObservableCollection&lt;string&gt;(Enumerable.Repeat("Word", 4));
          ItemsF = new ObservableCollection&lt;string&gt;(Enumerable.Repeat("Word", 1000000));
  
          DataContext = this;
          InitializeComponent();
      }
  
      void OnAddClick(object _sender, EventArgs _args)
      {
          ItemsA.Add("new");
          ItemsB.Add("new");
          ItemsC.Add("new");
          ItemsD.Add("new");
          ItemsE.Add("new");
          ItemsF.Add("new");
      }
  
      void OnRemoveClick(object _sender, EventArgs _args)
      {
          ItemsA.Remove(ItemsA.LastOrDefault());
          ItemsB.Remove(ItemsB.LastOrDefault());
          ItemsC.Remove(ItemsC.LastOrDefault());
          ItemsD.Remove(ItemsD.LastOrDefault());
          ItemsE.Remove(ItemsE.LastOrDefault());
          ItemsF.Remove(ItemsF.LastOrDefault());
      }
  }
  </code></pre>