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