How to Group Listview items that are added through XAML? - wpf

I have a Listview in XAML with few Listview Items that are added through XAML only and my listview has no other Items source.I want to group the items into two group.My Listview code is something like this
<ListView Name="List">
<ListViewItem Content="Apple" />
<ListViewItem Content="Orange" />
<ListViewItem Content="Tomato" />
<ListViewItem Content="Potato" />
<ListView />
I want to group them into two sets .Is this possible.

I would say if you want to group items in ListView you will not go around the CollectionViewSource. It's easier to fill it in ViewModel, but if you haven't VM you can also create one in XAML.
<StackPanel>
<StackPanel.Resources>
<x:Array x:Key="arr" Type="{x:Type ListViewItem}">
<ListViewItem Content="Orange" Tag="Meal"/>
<ListViewItem Content="Apple" Tag="Meal"/>
<ListViewItem Content="Cat" Tag="Pets"/>
<ListViewItem Content="Dog" Tag="Pets"/>
<ListViewItem Content="Fish" Tag="Diverse"/>
<ListViewItem Content="Duck" Tag="Diverse"/>
</x:Array>
<CollectionViewSource x:Key="CVS" Source="{DynamicResource arr}">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="Tag" />
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
</StackPanel.Resources>
<ListView ItemsSource="{Binding Source={StaticResource CVS}}">
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<Label Content="{Binding Name}"/>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</ListView.GroupStyle>
</ListView>
</StackPanel>

Related

Use CollectionViewSource with TabControl

I'm trying to group and display the items of an ObservableCollection, just by using XAML code. It works well using a simple CollectionViewSource and a ListBox[1].
Actually, I would prefer to display the group's content in a tabcontrol. Google led me to the following social.msdn article wich presents a workaround to display the groups as a TabControl using code behind:
https://social.msdn.microsoft.com/Forums/vstudio/en-US/e073f275-0826-4fca-b9da-e310ccf1713e/wpf-grouping?forum=wpf
However, as I'm using MVVM and must rely on xaml only, I can't get it to work. Actually, the CollectionViewSource populates the groups (the TabControl shows the correct tabItemHeaders), but clicking on any of these TabItems freezes the application. Here's what I've tried:
<StackPanel x:Key="ModulSelectInputParameterView">
<StackPanel.Resources>
<CollectionViewSource x:Key="cvs" x:Name="collectionViewSource" Source="{Binding ReferencedLmtItem.ModulInputParameterCollection }">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="Category"/>
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
</StackPanel.Resources>
<Grid >
<TabControl ItemsSource="{Binding Source={StaticResource cvs}, Path=Groups, Mode=OneWay}" DataContext="{Binding Source={StaticResource cvs}, Mode=OneWay}">
<!-- First Level -->
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<ListBox ItemsSource="{Binding Items}">
Second Level
<ListBox.ItemTemplate>
<DataTemplate>
<Expander Header="{Binding Name}">
<ListBox ItemsSource="{Binding Items}">
The Item of the Collection
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Key}"/>
<TextBlock Text=" - "/>
<TextBlock Text="{Binding Value.Comment}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Expander>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</Grid>
</StackPanel>
[1]: This peace of xaml does work as expected, but uses a wrappanel to display groups contents:
<StackPanel x:Key="ModulSelectInputParameterView">
<StackPanel.Resources>
<CollectionViewSource x:Key="cvs" x:Name="collectionViewSource" Source="{Binding ReferencedLmtItem.ModulInputParameterCollection }">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="Category"/>
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
</StackPanel.Resources>
<ListBox ItemsSource="{Binding Source={StaticResource cvs}}" VerticalContentAlignment="Top" ItemContainerStyle="{StaticResource ModulSelectInputParameterListBoxItemContainerStyle}" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListBox.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<Border BorderBrush="DarkGray" BorderThickness="2" Margin="2">
<TextBlock FontWeight="Bold" FontSize="14" Text="{Binding Path=Name}" HorizontalAlignment="Center" MinWidth="100"/>
</Border>
</DataTemplate>
</GroupStyle.HeaderTemplate>
<GroupStyle.Panel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" Margin="2"/>
</ItemsPanelTemplate>
</GroupStyle.Panel>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Border BorderThickness="2" BorderBrush="DarkGray">
<StackPanel>
<ContentPresenter Content="{TemplateBinding ContentControl.Content}" ContentTemplate="{TemplateBinding ContentControl.ContentTemplate}" ContentStringFormat="{TemplateBinding ContentControl.ContentStringFormat}" />
<ItemsPresenter Margin="2,0,2,2" />
</StackPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</ListBox.GroupStyle>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel IsItemsHost="True" Orientation="Vertical" VerticalAlignment="Top"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
</StackPanel>
I think there's something wrong with your binding your code should work.
To get the same items in both ListBoxes try to bind the second ListBox Itemssource to the first ListBox Itemssource like this :
<ListBox ItemsSource="{Binding Items}" Name="ListBox">
<ListBox.ItemTemplate>
<DataTemplate>
<Expander Header="{Binding Key}">
<ListBox ItemsSource="{Binding ItemsSource, ElementName=ListBox}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Key}"/>
<TextBlock Text=" - "/>
<TextBlock Text="{Binding Value}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Expander>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Thanks to Joh who helped with the appropriate DataBinding. However, the reason was totally different, a quick and dirty solution is given below[1]:
Basically, I was missing that the above mentioned tab control is nested within an outer Tab Control in my main window. I am not toally sure if the following description is entirely correct[1], but to my mind the reason is the following:
The outer TabControl uses a style to display its content. This content applies to a ViewModel which holds the above mentioned observable collection, which in turn should be the ItemsSource of the CollectionViewSource that feeds the inner tabControl with the groups.
As I have defined this style only in the outer TabControl.Resources, and missed to define a separate style for the inner tab Control, the inner tabcontrol inherits the outer style and tries to display its data using the same content.
This content is again another inner tabControl, which calls another inner tabControl and so on.
[1] Defining an empty style in the inenr tabControl.Resources solved the problem:
<TabControl.Resources>
<Style TargetType="TabItem">
</Style>
</TabControl.Resources>
I would be happy if someone could confirm this idea or provide some links to well known issues with shared styles in nested controls.

WPF ListView: items with and without icon

How to create a ListView in WPF so that some ListViewItems have icon and some not? I'd like to achive the ListView as in the below image.
I started with the below code but didn't get how to set the icon to the first 4 items only but not to other items. If the item has no icon, its text should left-align with any icon as shown in the image.
I also need to give the items with no icon slightly higher height than the items with icon.
<ListView>
<ListView.Items>
<ListViewItem Content="Save" />
<ListViewItem Content="Save As" />
<ListViewItem Content="Open" />
<ListViewItem Content="Close" />
<ListViewItem Content="Info" />
<ListViewItem Content="Recent" />
<ListViewItem Content="New" />
</ListView.Items>
<ListView.View>
<GridView>
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
' ???
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
Though i agree that this seems like a Menu and not a ListView as mentioned above , this would still be relevant with the use of MenuItems.
You should be aware that Content in Wpf can be set in 2 ways a Direct value like String , StaticResource , Binding (all are shorts of strings) , and a declarative value which you can describe a tree of elements as your content like iv'e shown below.
<ListView>
<ListView.Items>
<ListViewItem>
<ListViewItem.Content>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="2*"/>
</Grid.ColumnDefinitions>
<Image Source="SomeSource.png" />
<TextBlock Text="Save" Grid.Column="1" />
</Grid>
</ListViewItem.Content>
</ListViewItem>
<ListViewItem Content="Save As" />
<ListViewItem Content="Open" />
<ListViewItem Content="Close" />
<ListViewItem Content="Info" />
<ListViewItem Content="Recent" />
<ListViewItem Content="New" />
</ListView.Items>
</ListView>

Add an event on ListBoxItems build with ItemTemplate

I have a ListBox like this :
<ListBox DataContext="{Binding UpdateSourceTrigger=PropertyChanged}"
ItemsSource="{Binding UpdateSourceTrigger=PropertyChanged}"
ListBoxItem.Selected="ListBoxItem_Selected">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<DockPanel>
<Label Content="{Binding Path=Attribute[rdv].Value, UpdateSourceTrigger=PropertyChanged}" />
</DockPanel>
<DockPanel>
<Label Content="{Binding Path=Attribute[type].Value, UpdateSourceTrigger=PropertyChanged}" />
<Label Content="{Binding Path=Element[ville].Attribute[saisie].Value, UpdateSourceTrigger=PropertyChanged}" />
<Label Content=":" />
<Label Content="{Binding Path=Element[adresse].Attribute[saisie].Value, UpdateSourceTrigger=PropertyChanged}" />
</DockPanel>
<Separator />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I whant to rise an event when an ListeBoxItem is selected.
As you can see, I've tried with ListBoxItem.Selected="ListBoxItem_Selected" but it does not work.
Do you have an idea ?
Tanks by advance !
You handler doesn't get called because the Selected event is already handled by the ListBox. You should handle the SelectionChanged event in the ListBox instead:
<ListBox DataContext="{Binding UpdateSourceTrigger=PropertyChanged}"
ItemsSource="{Binding UpdateSourceTrigger=PropertyChanged}"
SelectionChanged="ListBox_SelectionChanged">
Alternatively, you can use an ItemContainerStyle to attach the handler to every ListBoxItem:
<ListBox DataContext="{Binding UpdateSourceTrigger=PropertyChanged}"
ItemsSource="{Binding UpdateSourceTrigger=PropertyChanged}">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<EventSetter Event="Selected" Handler="ListBoxItem_Selected"/>
</Style>
</ListBox.ItemContainerStyle>

How do I get the ActualWidth of the GridViewColumn

I have the following XAML:
<GridView x:Key="myGridView">
<GridViewColumn CellTemplate="{StaticResource myTemplate}"/>
</GridView>
<DataTemplate x:Key="myTemplate">
<ContentPresenter Content="{Binding}"/>
</DateTemplate>
When I use it, I want the contentpresenter to have the same width as the GridViewColumn. So far, I've tried using the RelativeSource binding but I can't seem to get the GridViewColumn at all.
How do I get the ActualWidth from the parent GridViewColumn from the ContentPresenter in my DataTemplate?
The problem with GridView is that there is no notion of cells as visual elements in the generated control hierarchy. In order for your ContentPresenter to be wide as the column you should set the HorizontalContentAlignment property of the ListViewItem to Stretch. This way the ContentPresenter will stretch to the available width, which is the width of the column.
Here is a sample XAML which illustrates this:
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<Grid.Resources>
<DataTemplate x:Key="leftTemplate">
<ContentPresenter HorizontalAlignment="Left" Content="{Binding}"/>
</DataTemplate>
<DataTemplate x:Key="rightTemplate">
<ContentPresenter HorizontalAlignment="Right" Content="{Binding}"/>
</DataTemplate>
<GridView x:Key="myGridView">
<GridViewColumn Width="150" CellTemplate="{StaticResource rightTemplate}" Header="Right"/>
<GridViewColumn Width="150" CellTemplate="{StaticResource leftTemplate}" Header="Left"/>
</GridView>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
</Grid.Resources>
<ListView Margin="10" View="{StaticResource myGridView}">
<ListViewItem Content="item 1"/>
<ListViewItem Content="item 2"/>
<ListViewItem Content="item 3"/>
<ListViewItem Content="item 4"/>
<ListViewItem Content="item 5"/>
</ListView>
</Grid>
</Page>

Populate ComboBox in GridView header with column data

I want to have a ComboBox in the header cells of my GridView (currently inside of a ListView) populated with the data from that column. To be used a bit like the 'auto filter' feature in Excel.
The problem I have is I can't get to the data for the column from inside the header cell. At the moment, I have something like this:
<Grid>
<Grid.Resources>
<DataTemplate x:Key="headerTemplate">
<StackPanel>
<ComboBox></ComboBox>
<TextBlock FontSize="16" Text="{Binding}" Grid.Row="1" HorizontalAlignment="Center"/>
</StackPanel>
</DataTemplate>
</Grid.Resources>
<ListView Width="400" Height="200">
<ListView.View>
<GridView ColumnHeaderTemplate="{StaticResource headerTemplate}">
<GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name}"/>
<GridViewColumn Header="Namespace" DisplayMemberBinding="{Binding Namespace}"/>
</GridView>
</ListView.View>
<x:Type TypeName="Visual"/>
<x:Type TypeName="UIElement"/>
<x:Type TypeName="FrameworkElement"/>
</ListView>
</Grid>
But I'm not sure how to get the data to populate the combo boxes in the header. What's the best way of doing this?
A quick and dirty way would be something like this:
<Grid.Resources>
<DataTemplate x:Key="headerTemplate">
<StackPanel>
<ComboBox ItemsSource="{Binding Path=Items, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListView}}}" DisplayMemberPath="{Binding}"/>
<TextBlock FontSize="16" Text="{Binding}" Grid.Row="1" HorizontalAlignment="Center"/>
</StackPanel>
</DataTemplate>
</Grid.Resources>
If you really need a filter-like list, you'll have to figure out how to get distinct values only.

Resources