I have a listview that has its ItemsSource bound to a collection of items. These items are grouped together using an Expander. I have been asked to format the header of the expander to reflect some information about the contents, specifically whether there is at least one member of each group where member.IsDirty==true
. I would like to change the Foreground
or the IsExpanded
properties based on this condition, so people can find the changes they made in the list even when the expander is collapsed (which is the preferable default display option).
Here are the windows resources used:
<DataTemplate DataType="{x:Type vm:OverViewModel}" x:Key="OV_DT" x:Name="OV_DT">
<control:OverView Width="{Binding ActualWidth,ElementName=ListWidth}"/>
</DataTemplate>
<CollectionViewSource x:Key="SortedOverViews" Source="{Binding OverViews}">
<CollectionViewSource.SortDescriptions>
<scm:SortDescription PropertyName="ParentName"/>
</CollectionViewSource.SortDescriptions>
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="ParentName"/>
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
and the list view itself looks like this:
<ListView ItemsSource="{Binding Source={StaticResource SortedOverViews}}"
ItemTemplate="{StaticResource OV_DT}">
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander IsExpanded="{Binding DataContext.Overviews_IsExpanded,RelativeSource={RelativeSource AncestorType=Window},Mode=OneTime}">
<Expander.Header>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Items[0].TargetOverView.Goods_Name}"
Foreground="{Binding Path=Items[0].Is_Intermediate,Converter={StaticResource boolColour}}"/>
</StackPanel>
</Expander.Header>
<Expander.Content>
<ItemsPresenter/>
</Expander.Content>
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</ListView.GroupStyle>
</ListView>
I thought for a while that I would need to somehow pass the contents of the collection group as a collection to a converter, returning something like myCollection.Where(x=>x.IsDirty).Any()
, but I cannot work out how to do this and now I don't know whether the contents of each group are even accessible in this way to make a collection.
Any guidance would be gratefully received! I have spent a fair amount of time looking at Expander and ListView tutorials online, but they only cover more simple examples, such as what I have already implemented.
CodePudding user response:
You can bind to Items
like this:
Foreground="{Binding Items, Converter={StaticResource converter}}
Converter:
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) =>
value is ReadOnlyObservableCollection<object> items
&& items.OfType<YourClass>()?.Any(x => x.IsDirty) == true;
The converter works perfectly when given a full collection, however, when the listview is first populated, the binding only evaluates a single item in each group
You could use a MultiValueConverter and bind to both Items
and Items.Count
:
<TextBlock ...>
<TextBlock.Foreground>
<MultiBinding Converter="{StaticResource converter}">
<Binding Path="Items" />
<Binding Path="Items.Count" />
</MultiBinding>
</TextBlock.Foreground>
</TextBlock>