Listview Groups. Group pictures under a title by Date Taken - c#

So I currently have a ListView which displays a collection of photos in it.
I would like to be able to now group these photos by the Date they were taken. I have the date taken property available to me on my "Photo" object. But I am unsure of how to group the photos. I don't really want the groups to be Expandable/Collapsable.
So far I have:
<ListView ItemsSource="{Binding FilteredPhotoFiles}" SelectedItem="{Binding SelectedPhotoVM.SelectedPhoto}">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Width="{Binding (FrameworkElement.ActualWidth), RelativeSource={RelativeSource AncestorType={x:Type ScrollContentPresenter}}}"
ItemWidth="{Binding (ListView.View).ItemWidth, RelativeSource={RelativeSource AncestorType={x:Type ListView}}}"
MinWidth="{Binding ItemWidth, RelativeSource={RelativeSource Self}}"
ItemHeight="{Binding (ListView.View).ItemHeight, RelativeSource={RelativeSource AncestorType={x:Type ListView}}}"/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<Grid Margin="2,4,2,4">
<Grid.Background>
<SolidColorBrush Color="LightGray" Opacity="0.5"/>
</Grid.Background>
<Image Source="{Binding PhotoFileInfo.FullName}" Width="300" Height="170" />
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
where FilteredPhotos is a List ordered by DateTaken

You can use CollectionViewSource to sort your collection on any property by adding GroupDescriptors in it.
For this first create CollectionViewSource under your resources section of window or panel whose source will bind to actual source i.e. FilteredPhotoFiles in your case. And add GroupDescriptions with propertyName set to DateTaken.
<CollectionViewSource x:Key="cvs" Source="{Binding FilteredPhotoFiles}">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="DateTaken"/>
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
Second you have to set GroupStyle on ListView that how you want it to group.
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</ListView.GroupStyle>
Complete XAML would look like this:
<StackPanel>
<StackPanel.Resources>
<CollectionViewSource x:Key="cvs" Source="{Binding FilteredPhotoFiles}">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="DateTaken"/>
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
</StackPanel.Resources>
<ListView ItemsSource="{Binding Source={StaticResource cvs}}" SelectedItem="{Binding SelectedPhotoVM.SelectedPhoto}">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Width="{Binding (FrameworkElement.ActualWidth), RelativeSource={RelativeSource AncestorType={x:Type ScrollContentPresenter}}}"
ItemWidth="{Binding (ListView.View).ItemWidth, RelativeSource={RelativeSource AncestorType={x:Type ListView}}}"
MinWidth="{Binding ItemWidth, RelativeSource={RelativeSource Self}}"
ItemHeight="{Binding (ListView.View).ItemHeight, RelativeSource={RelativeSource AncestorType={x:Type ListView}}}"/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<Grid Margin="2,4,2,4">
<Grid.Background>
<SolidColorBrush Color="LightGray" Opacity="0.5"/>
</Grid.Background>
<Image Source="{Binding PhotoFileInfo.FullName}" Width="300" Height="170" />
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</ListView.GroupStyle>
</ListView>
</StackPanel>

Related

Filter Control with Select all

I created the following control:
public ObservableCollection<User> Users { get; set; } = new();
<CollectionViewSource
x:Key="UsersKey"
IsLiveFilteringRequested="True"
Source="{Binding Users}"/>
<ItemsControl x:Name="ItemsControlUsers" ItemsSource="{Binding Source={StaticResource Users}}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding IsSelected}" Checked="CheckBox_OnChecked" Unchecked="CheckBox_OnUnchecked" Content="{Binding Name}"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
And this is how it looks:
And I want to add a CheckBox of Select all.
I would like it to look like this:
The most obvious solution would be to just use a StackPanel add a CheckBox before the ItemsControl. In this case the item does not belong to the ItemsControl. Whether this is acceptable or not depends on your requirements.
<StackPanel>
<StackPanel.Resources>
<CollectionViewSource
x:Key="UsersKey"
IsLiveFilteringRequested="True"
Source="{Binding StringItems}"/>
</StackPanel.Resources>
<StackPanel Orientation="Horizontal">
<CheckBox Content="Select all"/>
</StackPanel>
<ItemsControl x:Name="ItemsControlUsers">
<!-- ...other markup. -->
</ItemsControl>
</StackPanel>
Another solution is to use a CompositeCollection that allows you to bind multiple collections and also add single elements. All of the items are then part of the ItemsControl.
<Border>
<Border.Resources>
<CollectionViewSource
x:Key="UsersKey"
IsLiveFilteringRequested="True"
Source="{Binding Users}"/>
</Border.Resources>
<ItemsControl x:Name="ItemsControlUsers">
<ItemsControl.ItemsSource>
<CompositeCollection>
<StackPanel Orientation="Horizontal">
<CheckBox Content="Select all"/>
</StackPanel>
<CollectionContainer Collection="{Binding Source={StaticResource UsersKey}}"/>
</CompositeCollection>
</ItemsControl.ItemsSource>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding IsSelected}" Checked="CheckBox_OnChecked" Unchecked="CheckBox_OnUnchecked" Content="{Binding Name}"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Border>

ListBox Grouping WPF

I'm trying to group a listbox. And all I can get to show up is the header.
I've got a list list of 'Online Users' , which look like this.
public class OnlineUser{
public string Branch {get;set;}
public string FirstName{get;set;}
public string LastName{get;set;}
}
Then i populate the list with some users, and put that list into a ICollectionView 'FilterableOnlineUsers'
FilterableOnlineUsers = CollectionViewSource.GetDefaultView(OnlineUsers);
FilterableOnlineUsers.GroupDescriptions.Add(new PropertyGroupDescription("Branch"));
FilterableOnlineUsers.SortDescriptions.Add(new SortDescription("Branch", ListSortDirection.Descending));
And in my Xaml:
<ListBox SelectedItem="{Binding DataContext.SelectedUser" ItemsSource="{Binding DataContext.FilterableOnlineUsers" >
<ListBox.GroupStyle>
<GroupStyle />
</ListBox.GroupStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock>
<TextBlock.Text>
<MultiBinding StringFormat="{}{0} {1}">
<Binding Path="FirstName"></Binding>
<Binding Path="LastName"></Binding>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
All I can get to show up in the Listbox is the branch name. I can't get first name or lastname to show up underneath the group Descriptions..
Thanks.
You should define a CollectionViewSource in XAML resource Like Below and make the ItemsSource set to the CollectionViewSource,
<CollectionViewSource x:Key="ListBoxItems" Source="{Binding Path=ListOfOnlineUser}">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="Branch" />
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
In List Box
<ListBox ItemsSource="{Binding Source={StaticResource ListBoxItems}}"/>
Detail
Below is the ListBox show grouped branch and each branch is inside an expander which you can collapse and expand each groups.
<ListBox
Margin="0,0,5,0"
ItemsSource="{Binding Source={StaticResource ListBoxItems}}"
SelectedIndex="-1"
SelectedItem="{Binding SelectedBranch}">
<ListBox.GroupStyle>
<GroupStyle>
<GroupStyle.Panel>
<ItemsPanelTemplate>
<VirtualizingStackPanel Orientation="Vertical" />
</ItemsPanelTemplate>
</GroupStyle.Panel>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Expander
Padding="0"
BorderThickness="0"
Header="{Binding Name}"
IsExpanded="True">
<ItemsPresenter/>
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</ListBox.GroupStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<Border BorderThickness="0">
<Grid>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding FirstName}" />
<TextBlock Text="{Binding LastName}" />
</StackPanel>
</Grid>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Padding" Value="0" />
<Setter Property="Margin" Value="0" />
</Style>
</ListBox.ItemContainerStyle>
</ListBox>

How to handle selection event with a wrappanel

I'm currently on a C# WPF project and I display images in several rows (the blue heads)
The problem is that I can't select any of this items, I am using a MVVM pattern so the code behind must be as light as possible and I have to do eveything I can in the xaml file.
So I would like to be able to select images by clicking on them, I've tried to use event like "IsMouseOver" but I was only able to change the whole wrappanel and not single items.
Here is the code I use:
<Grid Grid.Row="1" Height="Auto">
<Grid.Background>
<LinearGradientBrush>
<LinearGradientBrush.GradientStops>
<GradientStop Color="#252525" />
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Grid.Background>
<ItemsControl Background="Transparent" Foreground="AntiqueWhite" BorderBrush="Transparent"
HorizontalContentAlignment="Stretch" ItemsSource="{Binding Source={x:Static Context:Session.CurrentSession}, Path=CurrentProject.Models}">
<ItemsControl.ContextMenu>
<ContextMenu>
<MenuItem Header="Delete" Command="{Binding DeleteModel3DCommand}" CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=ContextMenu}, Path=PlacementTarget.SelectedItem}"/>
</ContextMenu>
</ItemsControl.ContextMenu>
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding SelectModel3DCommand}" CommandParameter="{Binding Path=SelectedItem, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ListBox}}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Image Source="/McKineap;component/Resources/Images/logo-mckineap.png" Height="100"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</Grid>
I will take any suggestions you could have about the proper way to define a select event in my wrappanel, thanks for your time !
ItemsControl items wasn't meant to be selectable, that's why selection events and selection features are missing, more specifically ItemsControl doesn't inherit from Selector class which allow that, on the other hand ListBox and ListView do.
Change the ItemsControl to a ListView and you should be able to implement selection:
<ListView SelectionMode="Single" Background="Transparent" Foreground="AntiqueWhite" BorderBrush="Transparent"
HorizontalContentAlignment="Stretch" ItemsSource="{Binding Items}">
Edit
don't forget to disable the HorizontalScrollBar in the ListView in-order for the WrapPanel to work
<Grid Grid.Row="1" Height="Auto">
<Grid.Background>
<LinearGradientBrush>
<LinearGradientBrush.GradientStops>
<GradientStop Color="#252525" />
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Grid.Background>
<ListView SelectionMode="Single" ScrollViewer.HorizontalScrollBarVisibility="Disabled" Background="Transparent" Foreground="AntiqueWhite" BorderBrush="Transparent"
HorizontalContentAlignment="Stretch" ItemsSource="{Binding Items}">
<ListView.ContextMenu>
<ContextMenu>
<MenuItem Header="Delete" Command="{Binding DeleteModel3DCommand}" CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=ContextMenu}, Path=PlacementTarget.SelectedItem}"/>
</ContextMenu>
</ListView.ContextMenu>
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding SelectModel3DCommand}" CommandParameter="{Binding Path=SelectedItem, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ListBox}}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<ListView.ItemTemplate>
<DataTemplate>
<Image Source="refresh.png" Height="100"/>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
</ListView>
</Grid>
I try to do the same way but with a ListBox instead of a ListView and it works for me.
<Grid Grid.Row="1" Height="Auto">
<Grid.Background>
<LinearGradientBrush>
<LinearGradientBrush.GradientStops>
<GradientStop Color="#252525" />
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Grid.Background>
<ListBox Name="ModelsListBox" Background="Transparent" Foreground="AntiqueWhite" BorderBrush="Transparent" ScrollViewer.HorizontalScrollBarVisibility="Disabled"
HorizontalContentAlignment="Stretch" ItemsSource="{Binding Source={x:Static Context:Session.CurrentSession}, Path=CurrentProject.Models}">
<ListBox.ContextMenu>
<ContextMenu>
<MenuItem Header="Delete" Command="{Binding DeleteModel3DCommand}" CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=ContextMenu}, Path=PlacementTarget.SelectedItem}"/>
<MenuItem Header="Rename"/>
</ContextMenu>
</ListBox.ContextMenu>
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding SelectModel3DCommand}" CommandParameter="{Binding Path=SelectedItem, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ListBox}}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Margin="0,0,5,0" HorizontalAlignment="Left">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Image Source="/McKineap;component/Resources/Images/logo-mckineap.png" Grid.Column="0" HorizontalAlignment="Left" VerticalAlignment="Bottom" Height="55" Width="50"/>
<ListBoxItem Grid.Column="1" Content="{Binding NameWithoutExtension}" HorizontalAlignment="Stretch" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
</Grid>

image binding to the drodown values in wpf

In my xaml form i have a dropdown button the dropdown values will bind from database and my requirement is i want to add one image for each dropdown value in the dropdown box, how can get this.....
<ctrl:DropDownButton Grid.Column="1"
Height="25"
Text="Add Question"
Width="125"
Margin="5,0,10,0">
<ctrl:DropDownButton.DropDownContextMenu>
<ContextMenu ItemsSource="{Binding Source={StaticResource QuestionTypes}}">
<ContextMenu.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Background="WhiteSmoke"/>
</ItemsPanelTemplate>
</ContextMenu.ItemsPanel>
<ContextMenu.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Command" Value="{Binding DataContext.AddQuestionCommand,
RelativeSource={RelativeSource AncestorType=UserControl}}" />
<Setter Property="CommandParameter" Value="{Binding}" />
</Style>
</ContextMenu.ItemContainerStyle>
<ContextMenu.ItemTemplate>
<DataTemplate>
<Grid Margin="-20,0,-50,0">
<TextBlock Grid.Column="1" Text="{Binding Converter={StaticResource EnumDescriptionConverter}}"/>
</Grid>
</DataTemplate>
</ContextMenu.ItemTemplate>
</ContextMenu>
</ctrl:DropDownButton.DropDownContextMenu>
</ctrl:DropDownButton>
*I wrote this outside of VisualStudio so please check the syntax
you should add an Image element to your DataTempalte and using a converter for the enum value resolve the path to the image you would like to present.
<ContextMenu.ItemTemplate>
<DataTemplate>
<Grid Margin="-20,0,-50,0">
<Grid>
<Grid.ColumnDefenitions>
<ColumnDefenition />
<ColumnDefenition />
</Grid.ColumnDefenitions>
<Image Source="{Binding Path=., Converter={StaticResource EnumImageConverter}}" />
<TextBlock Grid.Column="1" Text="{Binding Path=., Converter={StaticResource EnumDescriptionConverter}}"/>
</Grid>
</DataTemplate>
</ContextMenu.ItemTemplate>

How to align auto generated content DataTemplate next to each other?

i want those buttons next to each other and not under each other
this is a mvvm, so the buttons are generated by a class
<ItemsControl ItemsSource="{Binding Pages}" Grid.Column="1">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<Button Margin="8" Content="{Binding Name}" Command="{Binding DataContext.ChangePageCommand, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" CommandParameter="{Binding}"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
You just have to change the default ItemsPanel from your ItemsControl into a horizontal oriented one, here's an example :
<ItemsControl ...>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<Button Margin="8" Content="{Binding Name}" Command="{Binding DataContext.ChangePageCommand, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" CommandParameter="{Binding}"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
By defining an horizontal StackPanel as your ItemsControl panel, each item will be aligned next to each other (Note that the default one is a vertical StackPanel).

Categories