Hiding expander when all content is collapsed
Asked Answered
S

4

11

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?

Stair answered 1/10, 2015 at 11:34 Comment(3)
If all children are visible then show the expander, otherwise collapse it sounds like a converter task. You already have property IsVisibleInMainWindow, change it when children are collapsed. Note: Items should be ObservableCollection.Phillips
Can you give us a more complete xaml including all expander?Restharrow
I think we do need more xaml.Xanthic
C
4

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.

Chemical answered 9/10, 2015 at 18:22 Comment(1)
I chose this answer as It worked almost perfectly out of the box in pure xaml. This is the simplest way of doing it I think although the others answers have merit in their own rightStair
U
3

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.

Unsightly answered 7/10, 2015 at 11:33 Comment(0)
A
1

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;
                }
            }
        }
    }
Aretta answered 7/10, 2015 at 10:42 Comment(0)
A
0

Use IMultiValueConverter implementation to convert items to visibility. If all items IsVisibleInMainScreen property return true the converter will return visible else hidden.

Use the converter in the same place U used to convert the first item in original example

Arcograph answered 7/10, 2015 at 17:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.