CollectionViewSource+PropertyGroupDescription - count of items in a group - wpf

In my WPF application I have a CollectionViewSource which is providing a view to a private ObservableCollection. The CollectionViewSource has a PropertyGroupDescription which is utilised in a ListBox to present data to the User's preference.
Using a ControlTemplate containing a Expander Control within the ListBox GroupStyle, the result is quite nice. However, I would like to show the number of items in each group in the Expander Header in addition to the Group Name. Any ideas on the binding path?
Regards,
Liam
<Style x:Key="basicGroupStyle" TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander Header="{Binding Name}" IsExpanded="True">
<ItemsPresenter/>
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<ListBox ItemsSource="{Binding Source={StaticResource myViewSource}}">
<ListBox.GroupStyle>
<GroupStyle ContainerStyle="{StaticResource basicGroupStyle}"/>
</ListBox.GroupStyle>
</ListBox>

you have to use property ItemCount
<Window x:Class="WpfApplication11.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Window.Resources>
<XmlDataProvider x:Key="data">
<x:XData>
<Animals xmlns="">
<Animal name="Dory" Species="Fish" />
<Animal name="Felix" Species="Cat" />
<Animal name="Fluffy" Species="Dog" />
<Animal name="Jake" Species="Snake" />
<Animal name="Mittens" Species="Cat" />
<Animal name="Murtle" Species="Turtle" />
<Animal name="Nemo" Species="Fish" />
<Animal name="Rex" Species="Dog" />
<Animal name="Rover" Species="Dog" />
<Animal name="Toonces" Species="Cat" />
</Animals>
</x:XData>
</XmlDataProvider>
<CollectionViewSource x:Key="animalsBySpecies" Source="{Binding Source={StaticResource data}, XPath=Animals/Animal}">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="#Species" />
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
</Window.Resources>
<DockPanel>
<ScrollViewer DockPanel.Dock="Bottom" VerticalScrollBarVisibility="Auto">
<ItemsControl ItemsSource="{Binding Source={StaticResource animalsBySpecies}}">
<ItemsControl.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<GroupBox >
<GroupBox.Header>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Text ="{Binding Name}" ></TextBlock>
<TextBlock Text="(" Grid.Column="1" Margin="15,0,0,0"></TextBlock>
<TextBlock Text="{Binding ItemCount}" Grid.Column="1" Margin="20,0,0,0"
HorizontalAlignment="Right" ></TextBlock>
<TextBlock Text=")" Margin="0,0,-5,0" Grid.Column="1
HorizontalAlignment="Right" ></TextBlock>
</Grid>
</GroupBox.Header>
<ItemsPresenter />
</GroupBox>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</ItemsControl.GroupStyle>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding XPath=#name}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</DockPanel>
</Window>

I updated the Group Style as follows. I found that I could not use a MultiBinding in the Expander.Header property, nothing was displayed on screen, I needed to include an intermediate TextBlock instead.
<Style x:Key="basicGroupStyle" TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander IsExpanded="True">
<Expander.Header>
<TextBlock>
<TextBlock.Text>
<MultiBinding StringFormat="{}{0} ({1})">
<Binding Path="Name"/>
<Binding Path="ItemCount"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</Expander.Header>
<ItemsPresenter/>
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

Related

Setting textblock width based on another control width

My task is to set the Expander's TextBlock width based on a column of the datagrid which is contained in. The XAML is this:
<DataGrid x:Name="GridData" Grid.Column="4" Height="287" ColumnWidth="Auto" CanUserResizeColumns="False">
<DataGrid.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander Background="Red" HorizontalAlignment="Left" HorizontalContentAlignment="Left">
<Expander.Header>
<StackPanel Orientation="Horizontal">
<TextBlock Width="{Binding ElementName=GridData.colTimeAlarm, Path=ActualWidth}" Background="Azure" Text="{Binding StringFormat=hh\\:mm\\:ss, Converter={StaticResource SumTimes}, ConverterParameter=TimeAlarm}" />
</StackPanel>
</Expander.Header>
<Expander.Content>
<ItemsPresenter />
</Expander.Content>
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</DataGrid.GroupStyle>
<DataGrid.Columns>
<DataGridTextColumn Width="Auto" MinWidth="80" Binding="{Binding Data, UpdateSourceTrigger=PropertyChanged}" Header="#_151_Data" Foreground="Black" IsReadOnly="True" x:Name="colTimeAlarm" />
</DataGrid.Columns>
In the TextBlock I binded the width property as ActualWidth of the colum specified but it doesn't work. What's wrong?

How to set DataTemplate width to the width of the Listbox?

Here's the example from MSDN.
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Margin" Value="0,0,0,5"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander IsExpanded="True" BorderBrush="#FFA4B97F"
BorderThickness="0,0,0,1">
<Expander.Header>
<DockPanel>
<TextBlock FontWeight="Bold" Text="{Binding Path=Name}"
Margin="5,0,0,0" Width="100"/>
<TextBlock FontWeight="Bold"
Text="{Binding Path=ItemCount}"/>
</DockPanel>
</Expander.Header>
<Expander.Content>
<ItemsPresenter />
</Expander.Content>
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
How can I make it occupy the whole width of the listbox? (HorizontalScrollBar is disabled)?
What I exactly need is DockPanel to be streched.
You have to include the reference of PresentationFramework.Aero in your project.
After this in your ListBox you have to insert this attribute:
<ListBox
xmlns:Microsoft_Windows_Themes="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero"
HorizontalContentAlignment="Stretch"
>
</ListBox>

Avoiding spaces while grouping with listbox

I am using listbox grouping, I want to avoid vertical spaces left between header and subelements, as shown below:
Here is the xaml for the same
<Grid>
<Grid.Resources>
<XmlDataProvider x:Key="Company" XPath="/Company">
<x:XData>
<Company xmlns="">
<Person Name="Jack" Role="CEO"/>
<Person Name="Tim" Role="PL" />
<Person Name="Jil" Role="PL" />
<Person Name="Jimmy" Role="PM" />
<Person Name="Joy" Role="PM" />
<Person Name="Jim" Role="PL" />
<Person Name="Jack" Role="PM" />
</Company>
</x:XData>
</XmlDataProvider>
<DataTemplate x:Key="categoryTemplate">
<TextBlock Text="{Binding Path=Name}" FontWeight="Bold" Background="ForestGreen" Margin="0,5,0,0"/>
</DataTemplate>
<DataTemplate x:Key="template">
<TextBlock Text="{Binding XPath=#Name}"/>
</DataTemplate>
<CollectionViewSource x:Key="cvs" Source="{Binding Source={StaticResource Company},XPath=Person}">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="#Role"/>
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
</Grid.Resources>
<ListBox Name="lst" ItemTemplate="{StaticResource template}" ItemsSource="{Binding Source={StaticResource cvs}}">
<ListBox.GroupStyle>
<GroupStyle HeaderTemplate="{StaticResource categoryTemplate}" />
</ListBox.GroupStyle>
</ListBox>
</Grid>
The ListBox will use GroupItems when you group and they have a default Margin set to 5,0,0,0. Also, ListBoxItem comes with a default Padding of 2,0,0,0. You can change either one, or both, of them like this
<ListBox ...>
<ListBox.Resources>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<StackPanel>
<ContentPresenter/>
<ItemsPresenter Margin="0,0,0,0"/>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.Resources>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="Padding" Value="0"/>
</Style>
</ListBox.ItemContainerStyle>
<!--...-->
</ListBox>
Check if this is fine for you. Just modified your template bit.
<DataTemplate x:Key="template">
<TextBlock Text="{Binding XPath=#Name}" Margin="-5,0,0,0"/>
</DataTemplate>

WPF: Groupstyle not working correctly because of Style and/or modified ListBox

I am trying to use a GroupStyle but instead of showing the groupname as a header over the items in the list it only shows the header and not the items.
It works fine until I applied a special style to the list. I then decided this style was pretty bogus so I created a UserControl fro this effect. The problem persists.
The purpose of the UserControl is to have a expending effect on the selected item where normally it could show some info and then more info when expanded.
UserControl:
<UserControl x:Class="MyProject.CustomUC.ExpandingList"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<ListBox x:Name="List"
ItemsSource="{Binding Path=Collection}">
<ListBox.Template>
<ControlTemplate TargetType="ListBox">
<Border BorderBrush="Blue"
BorderThickness="1"
CornerRadius="2"
Background="White">
<ScrollViewer Focusable="False">
<StackPanel Margin="2" IsItemsHost="True" />
</ScrollViewer>
</Border>
</ControlTemplate>
</ListBox.Template>
<ListBox.GroupStyle>
<GroupStyle HidesIfEmpty="True">
<GroupStyle.HeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Name}"/>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</ListBox.GroupStyle>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Border Margin="4" Name="Border" BorderBrush="Blue" BorderThickness="1" CornerRadius="5" Background="LightBlue">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<ContentPresenter Name="Head"
Margin="4"
Visibility="Visible"
ContentTemplate="{Binding ElementName=List, Path=DataContext.HeadTemplate}"/>
<Border Name="Tail" Visibility="Collapsed" Grid.Row="1" BorderThickness="0,1,0,0" BorderBrush="Blue">
<ContentPresenter Margin="4"
ContentTemplate="{Binding ElementName=List, Path=DataContext.TailTemplate}" />
</Border>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter TargetName="Tail" Property="Visibility" Value="Visible" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
</Grid>
</UserControl>
Using the UserControl:
<CustomUC:ExpandingList Collection="{Binding Path=List}">
<CustomUC:ExpandingList.HeadTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=DisplayName}" />
</DataTemplate>
</CustomUC:ExpandingList.HeadTemplate>
<CustomUC:ExpandingList.TailTemplate>
<DataTemplate DataType="{x:Type ViewModel:ElementViewModel}">
<TextBlock Text="{Binding Path=SomeOtherProperties}" />
</DataTemplate>
</CustomUC:ExpandingList.TailTemplate>
</CustomUC:ExpandingList>
This works if I change from the ExpandingList UserCotnrol:
<ListView ItemsSource="{Binding Path=List}" >
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=DisplayName}" />
</DataTemplate>
</ListView.ItemTemplate>
<ListView.GroupStyle>
<GroupStyle HidesIfEmpty="True">
<GroupStyle.HeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Name}"/>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</ListView.GroupStyle>
</ListView>
Anyone know what could be wrong?
In the ListBox template, replace:
<StackPanel Margin="2" IsItemsHost="True" />
with
<ItemsPresenter Margin="2" />
The ItemsPresenter class has extra logic for groups, that you lose if you're directly using a panel to display the items.

How to implement a XAML Radio Button control with an ObservableCollection source?

I have the following ComboBox element in XAML:
<ComboBox ItemsSource="{Binding CollectionControlValues}"
SelectedItem="{Binding CollectionControlSelectedValue, UpdateSourceTrigger=PropertyChanged}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Value}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
I would like to implement RadioButtons in the same way, like this:
PSEUDO-CODE:
<RadioButtons ItemsSource="{Binding CollectionControlValues}"
SelectedItem="{Binding CollectionControlSelectedValue, UpdateSourceTrigger=PropertyChanged}">
<RadioButtons .ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Value}" />
</DataTemplate>
</RadioButtons .ItemTemplate>
</RadioButtons >
However, the only WPF RadioButton implementations I can find are static like this.
<StackPanel x:Name="rbHolder1" Style="{StaticResource rbStackPanelStyle}">
<RadioButton Style="{StaticResource rbStyle}">RadioButton 1</RadioButton>
<RadioButton Style="{StaticResource rbStyle}">RadioButton 2</RadioButton>
<RadioButton Style="{StaticResource rbStyle}">RadioButton 3</RadioButton>
<RadioButton Style="{StaticResource rbStyle}">...</RadioButton>
</StackPanel>
How can I create a RadioButton control which is not static like the above but instead gets its data from its ItemsSource property as in the above ComboBox example?
Use ItemsControl and DataTemplate:
<ItemsControl ItemsSource="{Binding CollectionControlValues}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<RadioButton Content="{Binding Value}" IsChecked="{Binding SomeProperty}" GroupName="name"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
code in window1.xaml file
<Window x="RadioButton.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:local ="clr-namespace:RadioButton"
Title="Window1" Height="300" Width="300" Loaded="Window_Loaded">
<StackPanel>
<StackPanel.Resources>
<ObjectDataProvider x:Key="RadioOptions" MethodName="GetValues" ObjectType="{x:Type sys:Enum}">
<ObjectDataProvider.MethodParameters>
<x:Type TypeName="local:RadioOption" />
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
<Style x:Key="RadioButtonList" TargetType="{x:Type ListBox}">
<Setter Property="BorderBrush" Value="{x:Null}" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="ItemContainerStyle">
<Setter.Value>
<Style TargetType="{x:Type ListBoxItem}" >
<Setter Property="Margin" Value="2" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<Border Background="Transparent">
<RadioButton Focusable="False"
IsHitTestVisible="False"
IsChecked="{TemplateBinding IsSelected}">
<ContentPresenter />
</RadioButton>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Setter.Value>
</Setter>
</Style>
</StackPanel.Resources>
<ListBox Margin="37,20,28,58" Name="listBox1" Style="{StaticResource RadioButtonList}" ItemsSource="{Binding Source={StaticResource RadioOptions}}" />
</StackPanel>
this line
<ListBox` Margin="37,20,28,58" Name="listBox1" Style="{StaticResource RadioButtonList}"
ItemsSource="{Binding Source={StaticResource RadioOptions}}"
is using itemsource property
C# code
namespace RadioButton
{
public enum RadioOption
{
option1,
option2,
option3,
option4
}
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
}
}
i think this will help you out..

Resources