Background
I'm trying to create a control with a file explorer at the top and TreeView
at the bottem separated by a GridSplitter
. The user can change the size of the file explorer, but not beyond a minimum value. When the Expander
is collapsed, the TreeView
gets all the room. When the Expander
is expanded it goes back to the size that it was before it collapsed.
Example
Using this post I tried to make a working example.
<Grid Margin="3">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto" />
<RowDefinition Height="3*" />
</Grid.RowDefinitions>
<Border BorderBrush="Red" BorderThickness="3">
<Grid Grid.Row="0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition>
<RowDefinition.Style>
<Style TargetType="{x:Type RowDefinition}">
<Setter Property="Height" Value="*" />
<Setter Property="MinHeight" Value="150" />
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=MyExpander, Path=IsExpanded}" Value="False">
<Setter Property="Height" Value="24" />
<Setter Property="MinHeight" Value="24" />
</DataTrigger>
</Style.Triggers>
</Style>
</RowDefinition.Style>
</RowDefinition>
</Grid.RowDefinitions>
<Button Grid.Row="0" Margin="3" Content="Button 1" HorizontalAlignment="Stretch"/>
<Expander x:Name="MyExpander" Grid.Row="1" Margin="3" IsExpanded="True">
<Border BorderBrush="Blue" BorderThickness="3"/>
</Expander>
</Grid>
</Border>
<GridSplitter
Grid.Row="1"
Height="3"
HorizontalAlignment="Stretch"
Margin="3,0,3,0"/>
<Border Grid.Row="2" BorderBrush="Green" BorderThickness="3"/>
</Grid>
The resizing works at first when I collapse and expand the GridSplitter
.
Problem 1
After the GridSplitter
is moved, the resizing after collapse doesn't work anymore.
Problem 2
I can resize beyond the MinHeight
of 150 that I put in the RowDefinition
of the Expander
row.
How to solve these problems?
There are a couple reasons why your current XAML doesn't work.
Here is a working example, where I've restructured your XAML so that the GridSplitter can obey the MinHeight of your Expander.
I've also introduced a simple attached behavior class that will react to the Expander's Expanded and Collapsed events and will set the appropriate target RowDefinition's Height to Auto so that you get the auto resizing that you want.
The attached behavior class also persists the previous Height of the target RowDefinition and will restore it when the expander is expanded.
XAML
<Grid Margin="3">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition x:Name="ExpanderRow">
<RowDefinition.Style>
<Style TargetType="{x:Type RowDefinition}">
<Setter Property="MinHeight" Value="150" />
<Style.Triggers>
<DataTrigger
Binding="{Binding ElementName=MyExpander, Path=IsExpanded}"
Value="False">
<Setter Property="MinHeight" Value="40" />
</DataTrigger>
</Style.Triggers>
</Style>
</RowDefinition.Style>
</RowDefinition>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Border
Grid.Row="0"
Grid.RowSpan="2"
BorderBrush="Red"
BorderThickness="3" />
<Button
Grid.Row="0"
Margin="6"
HorizontalAlignment="Stretch"
Content="Button 1" />
<Expander x:Name="MyExpander"
Grid.Row="1"
Margin="6"
local:ExpanderRowHeightBehavior.IsEnabled="True"
local:ExpanderRowHeightBehavior.TargetRow="{Binding ElementName=ExpanderRow}"
IsExpanded="True">
<Border
BorderBrush="Blue"
BorderThickness="3" />
</Expander>
<GridSplitter
Grid.Row="2"
Height="3"
Margin="3,0,3,0"
HorizontalAlignment="Stretch"
VerticalAlignment="Center" />
<Border
Grid.Row="3"
BorderBrush="Green"
BorderThickness="3" />
</Grid>
ExpanderRowHeightBehavior.cs
using System.Windows;
using System.Windows.Controls;
namespace SO
{
public static class ExpanderRowHeightBehavior
{
#region IsEnabled (Attached Property)
public static readonly DependencyProperty IsEnabledProperty =
DependencyProperty.RegisterAttached(
"IsEnabled",
typeof(bool),
typeof(ExpanderRowHeightBehavior),
new PropertyMetadata(false, OnIsEnabledChanged));
public static bool GetIsEnabled(DependencyObject obj)
{
return (bool)obj.GetValue(IsEnabledProperty);
}
public static void SetIsEnabled(DependencyObject obj, bool value)
{
obj.SetValue(IsEnabledProperty, value);
}
private static void OnIsEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (!(d is Expander expander)) return;
expander.Collapsed += OnCollapsed;
expander.Expanded += OnExpanded;
}
#endregion
#region TargetRow (Attached Property)
public static readonly DependencyProperty TargetRowProperty =
DependencyProperty.RegisterAttached(
"TargetRow",
typeof(RowDefinition),
typeof(ExpanderRowHeightBehavior),
new PropertyMetadata(null));
public static RowDefinition GetTargetRow(DependencyObject obj)
{
return (RowDefinition)obj.GetValue(TargetRowProperty);
}
public static void SetTargetRow(DependencyObject obj, RowDefinition value)
{
obj.SetValue(TargetRowProperty, value);
}
#endregion
#region TargetRowPrevHeight (Attached Property)
public static readonly DependencyProperty TargetRowPrevHeightProperty =
DependencyProperty.RegisterAttached(
"TargetRowPrevHeight",
typeof(GridLength),
typeof(ExpanderRowHeightBehavior),
new PropertyMetadata(GridLength.Auto));
public static GridLength GetTargetRowPrevHeight(DependencyObject obj)
{
return (GridLength)obj.GetValue(TargetRowPrevHeightProperty);
}
public static void SetTargetRowPrevHeight(DependencyObject obj, GridLength value)
{
obj.SetValue(TargetRowPrevHeightProperty, value);
}
#endregion
private static void OnCollapsed(object sender, RoutedEventArgs e)
{
if (!(sender is Expander expander)) return;
var targetRow = GetTargetRow(expander);
if (targetRow == null) return;
SetTargetRowPrevHeight(expander, targetRow.Height);
targetRow.Height = GridLength.Auto;
}
private static void OnExpanded(object sender, RoutedEventArgs e)
{
if (!(sender is Expander expander)) return;
var targetRow = GetTargetRow(expander);
if (targetRow == null) return;
var targetRowPrevHeight = GetTargetRowPrevHeight(expander);
targetRow.Height = targetRowPrevHeight;
}
}
}