I've done it using two converters (HeaderConverter, HC and FooterConverter, FC) BUT I don't like the solution because it requires several nested if else
and sets foreground and fontsize several times without any reason. With ContentControl
in both header and footer:
<ListBox.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="GroupItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="GroupItem">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ContentControl x:Name="header" Content="{Binding Converter={StaticResource HC}}" FontWeight="Bold"/>
<ItemsPresenter x:Name="items" Grid.Row="1" Margin="15 0 0 0"/>
<ContentControl x:Name="footer" Grid.Row="2" Content="{Binding Converter={StaticResource FC}}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</ListBox.GroupStyle>
HC
does these:
public class HeaderConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var group = value as CollectionViewGroup;
var header = new TextBlock() { Text = group.Name.ToString(), FontWeight = FontWeights.Bold };
if ((!group.IsBottomLevel))
{
foreach (CollectionViewGroup subGroup in group.Items)
{
if (subGroup.IsBottomLevel)
{
header.Foreground = Brushes.Blue;
header.FontSize = 12;
}
else
{
foreach (CollectionViewGroup sSubGroup in subGroup.Items)
{
if(sSubGroup.IsBottomLevel)
{
header.Foreground = Brushes.Green;
header.FontSize = 14;
}
else
{
header.Foreground = Brushes.Red;
header.FontSize = 16;
header.Margin = new Thickness(0, 10, 0, 0);
}
}
}
}
}
return header;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
and FC
these:
public class FooterConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
int sum = 0;
var line = new Separator();
var sumBlock = new TextBlock() { HorizontalAlignment = HorizontalAlignment.Right, FontWeight = FontWeights.Bold };
var textBlock = new TextBlock() { FontWeight = FontWeights.Bold };
var group = value as CollectionViewGroup;
if (group.IsBottomLevel)
{
sum = ((IEnumerable<object>)group.Items).OfType<GroupedData>().Sum(x => x.Amount);
Grid.SetColumn(line, 1);
}
else
{
Grid.SetColumnSpan(line, 2);
foreach (CollectionViewGroup subGroup in group.Items)
{
if (subGroup.IsBottomLevel)
{
sum += ((IEnumerable<object>)subGroup.Items).OfType<GroupedData>().Sum(x => x.Amount);
sumBlock.Foreground = textBlock.Foreground = Brushes.Blue;
}
else
{
foreach (CollectionViewGroup sSubGroup in subGroup.Items)
{
if (sSubGroup.IsBottomLevel)
{
sum += ((IEnumerable<object>)sSubGroup.Items).OfType<GroupedData>().Sum(x => x.Amount);
sumBlock.Foreground = textBlock.Foreground = Brushes.Green;
}
else
{
sumBlock.Foreground = textBlock.Foreground = Brushes.Red;
foreach (CollectionViewGroup ssSubGroup in sSubGroup.Items)
sum += ((IEnumerable<object>)ssSubGroup.Items).OfType<GroupedData>().Sum(x => x.Amount);
}
}
}
}
}
textBlock.Text = "Total " + group.Name.ToString();
sumBlock.Text = sum.ToString();
Grid.SetColumn(sumBlock, 1);
var grid = new Grid()
{
RowDefinitions =
{
new RowDefinition(),
new RowDefinition()
},
ColumnDefinitions =
{
new ColumnDefinition(){ Width = new GridLength(1, GridUnitType.Star)},
new ColumnDefinition(){Width = new GridLength(50)}
},
Children = { line, textBlock, sumBlock },
Resources =
{
{
typeof(TextBlock),
new Style()
{
Setters =
{
new Setter()
{
Property = Grid.RowProperty,
Value = 1
}
}
}
}
}
};
return grid;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
and the output looks like this: