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?
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.
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.
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;
}
}
}
}
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