title: Collapse Grid Row in WPF
body: <p>I have created a custom WPF element extended from <code>RowDefinition</code> that should collapse rows in a grid when the <code>Collapsed</code> property of the element is set to <code>True</code>.</p>
  
  <p>It does it by using a converter and a datatrigger in a style to set the height of the row to 0. It is based on this <a href="https://stackoverflow.com/a/31984499/4601149">SO Answer</a>.</p>
  
  <p>In the example below, this works perfectly when the grid splitter is over half way up the window. However, when it is less than half way, the rows still collapse, but the first row does not expand. Instead, there is just a white gap where the rows used to be. This can be seen in the image below.</p>
  
  <p><a href="https://i.stack.imgur.com/bGaGg.png" rel="noreferrer"><img src="https://i.stack.imgur.com/bGaGg.png" alt="Picture shows under half, the bottom row doesn&#39;t disappear, but over half it does"></a></p>
  
  <p>Similarly, if <code>MinHeight</code> or <code>MaxHeight</code> is set on any of the rows that are collapsed, it no longer collapses the row at all. I tried to fix this by adding setters for these properties in the data trigger but it did not fix it.</p>
  
  <p>My question is what can be done differently so that it does not matter about the size of the rows or if <code>MinHeight</code> / <code>MaxHeight</code> are set, it is just able to collapse the rows?</p>
  
  <hr>
  
  <p>MCVE</p>
  
  <p><em>MainWindow.xaml.cs</em></p>
  
  <pre><code>using System;
  using System.ComponentModel;
  using System.Globalization;
  using System.Runtime.CompilerServices;
  using System.Windows;
  using System.Windows.Controls;
  using System.Windows.Data;
  
  namespace RowCollapsibleMCVE
  {
      public partial class MainWindow : INotifyPropertyChanged
      {
          public MainWindow()
          {
              InitializeComponent();
              DataContext = this;
          }
  
          public event PropertyChangedEventHandler PropertyChanged;
  
          private void OnPropertyChanged([CallerMemberName] string propertyName = null)
          {
              PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
          }
  
          private bool isCollapsed;
  
          public bool IsCollapsed
          {
              get =&gt; isCollapsed;
              set
              {
                  isCollapsed = value;
                  OnPropertyChanged();
              }
          }
      }
  
      public class CollapsibleRow : RowDefinition
      {
          #region Default Values
          private const bool COLLAPSED_DEFAULT = false;
          private const bool INVERT_COLLAPSED_DEFAULT = false;
          #endregion
  
          #region Dependency Properties
          public static readonly DependencyProperty CollapsedProperty =
              DependencyProperty.Register("Collapsed", typeof(bool), typeof(CollapsibleRow), new PropertyMetadata(COLLAPSED_DEFAULT));
  
          public static readonly DependencyProperty InvertCollapsedProperty =
              DependencyProperty.Register("InvertCollapsed", typeof(bool), typeof(CollapsibleRow), new PropertyMetadata(INVERT_COLLAPSED_DEFAULT));
          #endregion
  
          #region Properties
          public bool Collapsed {
              get =&gt; (bool)GetValue(CollapsedProperty);
              set =&gt; SetValue(CollapsedProperty, value);
          }
  
          public bool InvertCollapsed {
              get =&gt; (bool)GetValue(InvertCollapsedProperty);
              set =&gt; SetValue(InvertCollapsedProperty, value);
          }
          #endregion
      }
  
      public class BoolVisibilityConverter : IMultiValueConverter
      {
          public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
          {
              if (values.Length &gt; 0 &amp;&amp; values[0] is bool collapsed)
              {
                  if (values.Length &gt; 1 &amp;&amp; values[1] is bool invert &amp;&amp; invert)
                  {
                      collapsed = !collapsed;
                  }
  
                  return collapsed ? Visibility.Collapsed : Visibility.Visible;
              }
  
              return Visibility.Collapsed;
          }
  
          public object[] ConvertBack(object value, Type[] targetType, object parameter, CultureInfo culture)
          {
              throw new NotSupportedException();
          }
      }
  }
  </code></pre>
  
  <p><em>MainWindow.xaml</em></p>
  
  <pre><code>&lt;Window x:Class="RowCollapsibleMCVE.MainWindow"
          xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
          xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
          xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
          xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
          xmlns:local="clr-namespace:RowCollapsibleMCVE"
          mc:Ignorable="d"
          Title="MainWindow" Height="450" Width="800"&gt;
      &lt;Window.Resources&gt;
          &lt;Visibility x:Key="CollapsedVisibilityVal"&gt;Collapsed&lt;/Visibility&gt;
          &lt;local:BoolVisibilityConverter x:Key="BoolVisibilityConverter"/&gt;
  
          &lt;Style TargetType="{x:Type local:CollapsibleRow}"&gt;
              &lt;Style.Triggers&gt;
                  &lt;DataTrigger Value="{StaticResource CollapsedVisibilityVal}"&gt;
                      &lt;DataTrigger.Binding&gt;
                          &lt;MultiBinding Converter="{StaticResource BoolVisibilityConverter}"&gt;
                              &lt;Binding Path="Collapsed"
                                       RelativeSource="{RelativeSource Self}"/&gt;
                              &lt;Binding Path="InvertCollapsed"
                                       RelativeSource="{RelativeSource Self}"/&gt;
                          &lt;/MultiBinding&gt;
                      &lt;/DataTrigger.Binding&gt;
                      &lt;DataTrigger.Setters&gt;
                          &lt;Setter Property="MinHeight" Value="0"/&gt;
                          &lt;Setter Property="Height" Value="0"/&gt;
                          &lt;Setter Property="MaxHeight" Value="0"/&gt;
                      &lt;/DataTrigger.Setters&gt;
                  &lt;/DataTrigger&gt;
              &lt;/Style.Triggers&gt;
          &lt;/Style&gt;
      &lt;/Window.Resources&gt;
  
      &lt;Grid&gt;
          &lt;Grid.RowDefinitions&gt;
              &lt;RowDefinition Height="Auto"/&gt;
              &lt;RowDefinition Height="*"/&gt;
          &lt;/Grid.RowDefinitions&gt;
          &lt;CheckBox Content="Collapse Row"
                    IsChecked="{Binding IsCollapsed}"/&gt;
          &lt;Grid Row="1"&gt;
              &lt;Grid.RowDefinitions&gt;
                  &lt;local:CollapsibleRow Height="3*" /&gt;
                  &lt;local:CollapsibleRow Height="Auto" /&gt;
                  &lt;local:CollapsibleRow Collapsed="{Binding IsCollapsed}" Height="*" /&gt; &lt;!-- Using [MaxHeight="300"] breaks this completely --&gt;
              &lt;/Grid.RowDefinitions&gt;
              &lt;StackPanel Background="Red"/&gt;
  
              &lt;GridSplitter Grid.Row="1"
                            Height="10"
                            HorizontalAlignment="Stretch"&gt;
                  &lt;GridSplitter.Visibility&gt;
                      &lt;MultiBinding Converter="{StaticResource BoolVisibilityConverter}" &gt;
                          &lt;Binding Path="IsCollapsed"/&gt;
                      &lt;/MultiBinding&gt;
                  &lt;/GridSplitter.Visibility&gt;
              &lt;/GridSplitter&gt;
  
              &lt;StackPanel Background="Blue"
                          Grid.Row="2"&gt;
                  &lt;StackPanel.Visibility&gt;
                      &lt;MultiBinding Converter="{StaticResource BoolVisibilityConverter}" &gt;
                          &lt;Binding Path="IsCollapsed"/&gt;
                      &lt;/MultiBinding&gt;
                  &lt;/StackPanel.Visibility&gt;
              &lt;/StackPanel&gt;
          &lt;/Grid&gt;
      &lt;/Grid&gt;
  &lt;/Window&gt;
  </code></pre>