You will need to do this programmatically, not in xaml. This is because you want it to do two different things:
- Keep the last TextBlock close to the DataGrid if there are only a few items.
- Keep the last TextBlock visible if the DataGrid has a large amount of items.
Doing this will require you to hook into events in the code-behind, determine whether or not the last TextBlock disappears, then adjust the Height="Auto" or Height="*" accordingly on the RowDefinition, then UpdateLayout.
Here's a sample project. I replaced your DataGrid with a TextBlock for simplicity.
XAML:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Button Content="Make Grid.Row=2 Long, But Keep Text 3 Visible" Click="Button_Click" HorizontalAlignment="Center" Margin="5" Padding="5,10"/>
<Grid Grid.Row="1" x:Name="myGrid">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition x:Name="myRowDefinition" Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="This is Text 1" Background="Red"/>
<TextBlock Grid.Row="1" Text="This is Text 2" Background="Green"/>
<TextBlock Grid.Row="2" x:Name="myDataGrid" FontSize="64" Text="{Binding Output}" TextWrapping="Wrap" Background="Blue"/>
<TextBlock Grid.Row="3" x:Name="lastTextBlock" Text="This is Text 3" Background="Violet"/>
</Grid>
</Grid>
Code-behind:
public partial class MainWindow : Window, INotifyPropertyChanged
{
private string output;
public MainWindow()
{
InitializeComponent();
this.Loaded += OnLoaded;
this.DataContext = this;
}
/// <summary>
/// Handles the SizeChanged event of your data grid.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void MyDataGrid_SizeChanged(object sender, SizeChangedEventArgs e)
{
if (!IsUserVisible(lastTextBlock, this))
{
if (this.myRowDefinition.Height == GridLength.Auto)
{
// Edit the row definition and redraw it
this.myRowDefinition.Height = new GridLength(1, GridUnitType.Star);
}
}
else
{
if (this.myRowDefinition.Height != GridLength.Auto && CanDataGridBeSmaller(this.myRowDefinition.ActualHeight))
{
// If the datagrid can be smaller, change the row definition back to Auto
this.myRowDefinition.Height = GridLength.Auto;
}
}
this.UpdateLayout();
}
/// <summary>
/// It is possible for the DataGrid to take on more space than it actually needs. This can happen if you are messing with the window resizing.
/// So always check to make sure that if you can make the DataGrid smaller, that it stays small.
/// </summary>
/// <param name="actualHeight">the actual height of the DataGrid's row definition</param>
/// <returns></returns>
private bool CanDataGridBeSmaller(double actualHeight)
{
// Create a dummy control that is equivalent to your datagrid (for my purposes, I used a Textblock for simplicity, so I will recreate it fully here.
TextBlock dummy = new TextBlock() { TextWrapping = TextWrapping.Wrap, FontSize = 64, Text = this.Output };
dummy.Measure(new Size(this.myGrid.ActualWidth, this.myGrid.ActualHeight));
// Get the dummy height and compare it to the actual height
if (dummy.DesiredSize.Height < myRowDefinition.ActualHeight)
return true;
return false;
}
/// <summary>
/// This method determines if the control is fully visible to the user or not.
/// </summary>
private bool IsUserVisible(FrameworkElement element, FrameworkElement container)
{
if (!element.IsVisible)
return false;
Rect bounds = element.TransformToAncestor(container).TransformBounds(new Rect(0.0, 0.0, element.ActualWidth, element.ActualHeight));
Rect rect = new Rect(0.0, 0.0, container.ActualWidth, container.ActualHeight);
return rect.Contains(bounds);
}
/// <summary>
/// This is purely for setup.
/// </summary>
private void OnLoaded(object sender, RoutedEventArgs e)
{
this.myDataGrid.SizeChanged += MyDataGrid_SizeChanged;
this.Output = "This row is short, so Text 3 below me should be flush with my bottom.";
}
public event PropertyChangedEventHandler PropertyChanged;
public string Output { get => this.output; set { this.output = value; this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Output))); } }
private void Button_Click(object sender, RoutedEventArgs e)
{
this.Output = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
}
}
Example Output when you start:
Example Output after you click the button at the top: