char m
char m

Reputation: 8336

how to set heights of 3 DataGrids inside Extenders in one StackPanel in MVVM dynamically?

The heights of each DataGrid should depend on:

  1. Are other Extenders extended
  2. How many rows the DataGrid has
  3. If other Extenders are extended, how many rows other DataGrids have.
  4. Height of the StackPanel the Extenders are in

All Extenders should be always visible.

If e.g. opening only upmost Extender and its DataGrid has 100 rows the Height of the DataGrid should be almost the height of StackPanel but in the bottom the two other Extenders should be visible and upmost DataGrid scrollable.

When opening 2 or 3 Extenders the StackPanel should be "full" if there is enough data in DataGrids.

enter image description here

I tried to bind the height properties (for undermost expander Objec information)

    <Expander Header="Object information" IsExpanded="{Binding ObjectInformationExpanded}">
        <DataGrid Name="ObjectInformationGrid" CanUserAddRows="False" CanUserResizeColumns="True" CanUserSortColumns="True" IsReadOnly="True" 
               ItemsSource="{Binding SelectedObjectAttributes}" AutoGenerateColumns="False" 
                  Height="{Binding ObjectInformationHeight, Mode=TwoWay}"
                  ScrollViewer.CanContentScroll="True"
                  ScrollViewer.VerticalScrollBarVisibility="Visible">
            <DataGrid.Columns>
                <DataGridTextColumn Header="Field" Binding="{Binding Item1}" />
                <DataGridTextColumn Header="Value" Binding="{Binding Item2}" />
            </DataGrid.Columns>
        </DataGrid>
</Expander>

But now my ViewModel has lots of code that is related to presentation (View) and still didn't take the StackPanel's height into the calculation. Instead I set the hardcoded double values which can't be good solution.

Here is bit of simple code that updates the bound height Properties (here for upmost DataGrid only) when extenders are opened/closed and DataGrid bound collections are changed:

   private void UpdateHeights()
   {
        if (SelectedObjectsExpanded == true)
        {
            if (_selectedObjects.Count == 0)
            {
                SelectedObjectsHeight = double.NaN;
            }
            else if (_selectedObjects.Count < 8)
            {
                SelectedObjectsHeight = 100;
            }
            else
            {
                if (ObjectInformationExpanded && SelectedSwitchesExpanded)
                    SelectedObjectsHeight = 100;
                else
                    SelectedObjectsHeight = 200;
            }
        }
        //...

Thanks!

Upvotes: 3

Views: 275

Answers (1)

Rachel
Rachel

Reputation: 132548

Would something like this work?

The basic idea is put all 3 Expanders in a Grid containing 3 rows, and bind the RowDefinition.Height of each row to the Expander.IsExpanded. A Converter is used to set the value to Auto if the expander is collapsed (only take up the space needed), or * if it's expanded (take up all remaining space. If multiple rows are set to * height, share space evenly between them).

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="{Binding IsExpanded, ElementName=Expander1, Converter={x:Static MyBoolToGridSizeConverter}}" />
        <RowDefinition Height="{Binding IsExpanded, ElementName=Expander2, Converter={x:Static MyBoolToGridSizeConverter}}" />
        <RowDefinition Height="{Binding IsExpanded, ElementName=Expander3, Converter={x:Static MyBoolToGridSizeConverter}}" />
    </Grid.RowDefinitions>

    <Expander Grid.Row="0" x:Name="Expander1">
        <DataGrid />
    </Expander>
    <Expander Grid.Row="1" x:Name="Expander2">
        <DataGrid />
    </Expander>
    <Expander Grid.Row="2" x:Name="Expander3">
        <DataGrid />
    </Expander>
</Grid>
public class BoolToGridSizeConverter : IValueConverter
{
    public object Convert(object value, Type targetType, 
        object parameter, CultureInfo culture)
    {
        if (value is bool && (bool)value)
            return new GridLength(1, GridUnitType.Star);

        return GridLength.Auto;
    }

    public object ConvertBack(object value, Type targetType, 
        object parameter, CultureInfo culture)
    {
        // this is not needed
    }
}

Upvotes: 2

Related Questions