I have a WPF application that needs to be sort of responsive. What I want to have is a DataGrid
inside a Grid
. When the window scales down, I want the Grid
to resize first, and then the DataGrid
. Here is what I achieved so far:
In the gif on top you can see that the Grid
resizes first, and when it reaches it's minimum scaling size, it goes over the bottom DataGrid
. Not exactly what I want because I want to scale the layout first, then show a scrollbar in the DataGrid
instead of the layout just going over it. So I tried the following:
Right here you can see that it shows the scrollbars like I want. The only thing is that it first resizes the DataGrid, and when it's done resizing the DataGrid, it starts to resize the Grid. I want it to be the other way around, first resize the grid, then resize the DataGrid and show the Scrollbars. So basically I'm searching for a solution that does the following:
So that comes down to the first Gif from this question, followed by scrollbars in the DataGrid
Is there any way to do this? It seems like I'm very close since it's a combination of those two things but I don't know how. Here's my code:
<Grid Grid.Row="1" HorizontalAlignment="Right" Grid.Column="0">
<Grid ShowGridLines="False">
<Grid.RowDefinitions>
<RowDefinition MaxHeight="50"/>
<RowDefinition Height="auto"/>
<RowDefinition MaxHeight="20"/>
<RowDefinition Height="auto"/>
<RowDefinition MaxHeight="5"/>
<RowDefinition Height="auto"/>
<RowDefinition MaxHeight="5"/>
<RowDefinition Height="auto"/>
<RowDefinition MaxHeight="5"/>
<RowDefinition Height="auto"/>
<RowDefinition MaxHeight="50"/>
<RowDefinition Height="auto"/>
<RowDefinition MaxHeight="50"/>
<RowDefinition Height="auto"/>
<RowDefinition MaxHeight="5"/>
<RowDefinition Name="DataGridRow" Height="*" MaxHeight="240" />
</Grid.RowDefinitions>
<Label Grid.Row="1" FontSize="24">Test</Label>
<Label Grid.Row="3" Content="Test"/>
<ComboBox Grid.Row="5" MaxWidth="500" MinWidth="300" HorizontalAlignment="Left" />
<Label Grid.Row="7" Content="Test"/>
<ComboBox Grid.Row="9" MaxWidth="500" MinWidth="300" HorizontalAlignment="Left"/>
<Separator Grid.Row="11"/>
<Label Grid.Row="13" Content="Test" />
<Grid Grid.Row="15">
<DataGrid
RowHeight="40"
CanUserAddRows="False"
x:Name="dataGrid"
AutoGenerateColumns="False"
CanUserResizeColumns="True"
HeadersVisibility="None"
GridLinesVisibility="None"
ScrollViewer.CanContentScroll="True"
ScrollViewer.VerticalScrollBarVisibility="Visible"
ScrollViewer.HorizontalScrollBarVisibility="Auto"
<DataGrid.Columns>
<DataGridTextColumn IsReadOnly="True" Width="*" Binding="{Binding Name}">
<DataGridTextColumn.ElementStyle>
<Style TargetType="TextBlock">
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Margin" Value="2,0,0,0"/>
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Grid>
</Grid>
In those cases where solving the layout equation is no longer possible with the default building blocks, you can do the calculations yourself. Letting a converter calculate the available space that's left.
The remaining problem is twofold:
The DataGrid
may only shrink when the star sized rows have
ActualHeight
0.
Solution: DataGrid.Style
that sets dataGrid.MaxHeight
when
triggerGrid.ActualHeight
= 0 (triggerGrid
occupies a star sized
row).
When we're giving a MinHeight
to DataGridRow
it won't shrink when
the dataGrid.ActualHeight
shrinks (it steals the shrunk height
back).
Solution: RowDefinition.Style
that sets its MinHeight
to
dataGrid.ActualHeight
when triggerGrid.ActualHeight
= 0 and to the
fixed value otherwise.
I've set the Controls' BackGround
colors to illustrate when the star sized height (remaining white space) becomes 0.
<Window
...
Width="200" Height="450">
<Window.Resources>
<local:HeightConverter x:Key="HeightConverter" />
<local:IsEqualToZeroConverter x:Key="IsEqualToZeroConverter" />
</Window.Resources>
<!--MainGrid-->
<Grid x:Name="mainGrid">
<Grid ShowGridLines="False">
<Grid.RowDefinitions>
<RowDefinition MaxHeight="50"/>
<RowDefinition Height="auto"/>
<RowDefinition MaxHeight="20"/>
<RowDefinition Height="auto"/>
<RowDefinition MaxHeight="5"/>
<RowDefinition Height="auto"/>
<RowDefinition MaxHeight="5"/>
<RowDefinition Height="auto"/>
<RowDefinition MaxHeight="5"/>
<RowDefinition Height="auto"/>
<RowDefinition MaxHeight="50"/>
<RowDefinition Height="auto"/>
<RowDefinition MaxHeight="50"/>
<RowDefinition Height="auto"/>
<RowDefinition MaxHeight="5"/>
<RowDefinition x:Name="dataGridRow" Height="Auto">
<RowDefinition.Style>
<Style TargetType="{x:Type RowDefinition}">
<Style.Triggers>
<DataTrigger Binding="{Binding ActualHeight, ElementName=triggerGrid, Converter={StaticResource IsEqualToZeroConverter}}" Value="True">
<Setter Property="MinHeight" Value="{Binding ActualHeight, ElementName=dataGrid}"/>
</DataTrigger>
<DataTrigger Binding="{Binding ActualHeight, ElementName=triggerGrid, Converter={StaticResource IsEqualToZeroConverter}}" Value="False">
<Setter Property="MinHeight" Value="80.0"/>
</DataTrigger>
</Style.Triggers>
</Style>
</RowDefinition.Style>
</RowDefinition>
</Grid.RowDefinitions>
<Label x:Name="bigLabel" Grid.Row="1" FontSize="24" Background="LightGray">Test</Label>
<Label x:Name="regularLabel" Grid.Row="3" Content="Test" Background="LightGray"/>
<ComboBox x:Name="comboBox" Grid.Row="5" MaxWidth="500" MinWidth="300" HorizontalAlignment="Left" Background="LightGray" />
<Label Grid.Row="7" Content="Test" Background="LightGray"/>
<ComboBox Grid.Row="9" MaxWidth="500" MinWidth="300" HorizontalAlignment="Left" Background="LightGray"/>
<Separator x:Name="separator" Grid.Row="11"/>
<Label Grid.Row="13" Content="Test" Background="LightGray"/>
<!--TriggerGrid-->
<Grid Grid.Row="14" x:Name="triggerGrid"/>
<Grid Grid.Row="15">
<DataGrid
x:Name="dataGrid"
RowHeight="40"
CanUserAddRows="False"
AutoGenerateColumns="False"
CanUserResizeColumns="True"
HeadersVisibility="None"
GridLinesVisibility="None"
ScrollViewer.CanContentScroll="True"
ScrollViewer.VerticalScrollBarVisibility="Visible"
ScrollViewer.HorizontalScrollBarVisibility="Auto">
<DataGrid.Style>
<Style TargetType="{x:Type DataGrid}">
<Style.Triggers>
<DataTrigger Binding="{Binding ActualHeight, ElementName=triggerGrid}" Value="0">
<Setter Property="MaxHeight">
<Setter.Value>
<MultiBinding Converter="{StaticResource HeightConverter}">
<Binding ElementName="mainGrid" Path="ActualHeight"/>
<Binding ElementName="bigLabel" Path="ActualHeight"/>
<Binding ElementName="regularLabel" Path="ActualHeight"/>
<Binding ElementName="comboBox" Path="ActualHeight"/>
<Binding ElementName="separator" Path="ActualHeight"/>
</MultiBinding>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.Style>
<DataGrid.Columns>
<DataGridTextColumn IsReadOnly="True" Width="*" Binding="{Binding Name}">
<DataGridTextColumn.ElementStyle>
<Style TargetType="TextBlock">
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Margin" Value="2,0,0,0"/>
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Grid>
</Grid>
</Window>
Converters
public class HeightConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
// only 1 check at startup (Debugging)
if ((double)values[1] == 0.0) return 0.0;
double mainGridHeight = (double)values[0];
double bigLabelHeight = (double)values[1];
double regularLabelHeight = (double)values[2];
double comboBoxHeight = (double)values[3];
double separatorHeight = (double)values[4];
double dataGridHeight = mainGridHeight - bigLabelHeight - 2 * (regularLabelHeight + comboBoxHeight) - regularLabelHeight - separatorHeight;
if (dataGridHeight > 0.0) return dataGridHeight; else return 0.0;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
public class IsEqualToZeroConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return ((double)value == 0.0);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
This is not the exact answer. But, it will give an idea to achieve your need.
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="5" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<DockPanel>
<TextBlock DockPanel.Dock="Bottom" Grid.Row="1" FontSize="55" HorizontalAlignment="Center" VerticalAlignment="Center" TextWrapping="Wrap" MaxHeight="240">DataGrid</TextBlock>
<Separator DockPanel.Dock="Bottom"/>
<TextBlock DockPanel.Dock="Bottom" FontSize="55" HorizontalAlignment="Center" VerticalAlignment="Center" TextWrapping="Wrap">Top</TextBlock>
</DockPanel>
<GridSplitter Grid.Row="1" Height="5" HorizontalAlignment="Stretch" />
<TextBlock Grid.Row="2" FontSize="55" HorizontalAlignment="Center" VerticalAlignment="Center" TextWrapping="Wrap">Bottom</TextBlock>
</Grid>
Hope that helps.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With