Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Hiding expander when all content is collapsed

I have A WPF Datagrid that has a Collection View Source with 3 levels of grouping on it.

I have styled the datagrid to use 3 expanders such that it looks like this:

Level 1 Expander
<content>
    Level 2 Expander
    <content>
        Level 3 Expander
        <content>

Level 2 and Level 1 are just title of the groups

I have a second control that allows the user to show and hide level 3 items which works by binding the Level 3 expander to a Boolean "IsVisible" property in the object behind.

       <!--  Style for groups under the top level. this is the style for how a sample is displayed  -->
        <GroupStyle>
            <GroupStyle.ContainerStyle>
                <Style TargetType="{x:Type GroupItem}">
                    <Setter Property="Margin" Value="0,0,0,0" />
                    <Setter Property="Template">
                        <Setter.Value>
                            <ControlTemplate TargetType="{x:Type GroupItem}">

                                <!--  The parent control that determines whether or not an item needs to be displayed. This holds all of the sub controls displayed for a sample  -->
                                <Expander Margin="2"
                                          Background="{Binding Path=Name,
                                                               Converter={StaticResource SampleTypeToColourConverter}}"
                                          IsExpanded="True"
                                          Visibility="{Binding Path=Items[0].IsVisibleInMainScreen,
                                                               Converter={StaticResource BoolToVisibilityConverter}}">

This approach works fantasically well.

HOWEVER

If the user deselects all items in a level 3 expander, the Level 2 expander header still displays meaning that valuable real estate is used up showing the header of a group with no visible data.

What I would like is a way to bind the visibility of the level 2 expander to its child controls and say "If all children are visible then show the expander, otherwise collapse it"

Is this possible?

like image 900
Steven Wood Avatar asked Oct 01 '15 11:10

Steven Wood


3 Answers

I found a rather simple and clean way, yet not perfect, to achieve your goal. This should do the trick if hou don't have too much groups.

I've just added this trigger to the GroupItem ControlTemplate :

<ControlTemplate.Triggers>
    <DataTrigger Binding="{Binding ElementName=IP, Path=ActualHeight}" Value="0">
        <Setter Property="Visibility" Value="Hidden"/>
        <Setter Property="Height" Value="1"/>
    </DataTrigger>
</ControlTemplate.Triggers>

When the ItemsPresenter (IP) ActualSize drops to zero, it Will almost collapse the header.

Why almost ?

When the control gets initialized and before the binding occurs, the ItemPresenter ActualHeight is 0 and when Visibility is set to Collapsed, the ItemPresenter doesn't get rendered at all.

Using Visibility.Hidden allows the ItemsPresenter to go to the render phase and be mesured. I succedeed to drop Height to .4 px but I suspect this to be device dependant.

like image 104
xum59 Avatar answered Nov 03 '22 09:11

xum59


Assuming that you are using an MVVM sort of style, you could bind instead to a property of your group object that returns false if all of the children are invisible:

public bool AreChildrenVisible { get { return _children.Any(x=>x.IsVisibleInMainScreen); } }

Alternatively, pass the collection of Items through a Converter class to return Visibility depending on the aggregate status of all the subItems in the group.

like image 42
pete the pagan-gerbil Avatar answered Nov 03 '22 10:11

pete the pagan-gerbil


This isn't a direct answer as you would have to implement it specifically for your needs but previously I have used a an override of the Grid Control to create dynamic grid allocation of members, if there are no visible members it then hides the parent group box.

public class DynamicLayoutGrid : Grid
{

       protected override void OnInitialized(EventArgs e)
       {
                //Hook up the loaded event (this is used because it fires after the visibility binding has occurred)
             this.Loaded += new RoutedEventHandler(DynamicLayoutGrid_Loaded);

             base.OnInitialized(e);
        }


        void DynamicLayoutGrid_Loaded(object sender, RoutedEventArgs e)
        {
            int numberOfColumns = ColumnDefinitions.Count;
            int columnSpan = 0;
            int rowNum = 0;
            int columnNum = 0;
            int visibleCount = 0;

            foreach (UIElement child in Children)
            {
                //We only want to layout visible items in the grid
                if (child.Visibility != Visibility.Visible)
                {
                    continue;
                }
                else
                {
                    visibleCount++;
                }

                //Get the column span of the element if it is not in column 0 as we might need to take this into account
                columnSpan = Grid.GetColumnSpan(child);

                //set the Grid row of the element
                Grid.SetRow(child, rowNum);

                //set the grid column of the element (and shift it along if the previous element on this row had a rowspan greater than 0
                Grid.SetColumn(child, columnNum);

                //If there isn't any columnspan then just move to the next column normally
                if (columnSpan == 0)
                {
                    columnSpan = 1;
                }

                //Move to the next available column
                columnNum += columnSpan;

                //Move to the next row and start the columns again
                if (columnNum >= numberOfColumns)
                {
                    rowNum++;
                    columnNum = 0;
                }
            }

            if (visibleCount == 0)
            {
                if (this.Parent.GetType() == typeof(GroupBox))
                {
                    (this.Parent as GroupBox).Visibility = Visibility.Collapsed;
                }
            }
        }
    }
like image 44
Chris Avatar answered Nov 03 '22 09:11

Chris