C# ListView Relay Command Binding - c#

Can someone help me with the list view with grid. I want when you click on an element that a function is called and I get the current item. I already have the following XAML code:
<ListView Name="lstView" ItemsSource="{Binding Path=SimResults}" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding SelectedItemCommand}" CommandParameter="{Binding SelectedItem, ElementName=lstView}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Center"/>
</Style>
</ListView.ItemContainerStyle>
<ListView.View>
<GridView>
<GridView.Columns>
<GridViewColumn Header="FileUniqueID" Width="Auto" DisplayMemberBinding="{Binding Path=FileUniqueID}" />
<GridViewColumn Header="XML" Width="Auto" DisplayMemberBinding="{Binding Path=XML}" />
<GridViewColumn Header="Request" Width="Auto" HeaderStringFormat="" DisplayMemberBinding="{Binding Path=RequestShort}" />
<GridViewColumn Header="RequestDate" Width="Auto" DisplayMemberBinding="{Binding Path=RequestDate}" />
<GridViewColumn Header="Response" Width="Auto" DisplayMemberBinding="{Binding Path=ResponseShort}" />
<GridViewColumn Header="ResponseDate" Width="Auto" DisplayMemberBinding="{Binding Path=ResponseDate}" />
<GridViewColumn Header="ResendCounter" Width="Auto" DisplayMemberBinding="{Binding Path=ResendCounter}" />
</GridView.Columns>
</GridView>
</ListView.View>
</ListView>
In the cs file I have the following functions:
private Item selectedItem;
private RelayCommand selectedItemCommand;
this.selectedItemCommand = new RelayCommand(this.SelectedItems);
public Item SelectedItem
{
get { return selectedItem; }
set { selectedItem = value; OnPropertyChanged("SelectedTrends"); }
}
public RelayCommand SelectedItemCommand
{
get
{
return this.selectedItemCommand;
}
}
private void SelectedItems(object obj)
{
this.requestXml = this.selectedItem.DisplayName;
}
When I select an element the selectedItems and I can get the item.
Hi I tested the code above and it worked fine. But I have problem. I have different tabs, when I click a different tab with the left click it always runs the SelectionChanged event and I get a Null reference exception.

One solution is to create a property for the "SelectedItem" from the listView.
So, in your VM:
private YourType _selectedResult;
public YourType SelectedResult
{
get { return _selectedResult; }
set { _selectedResult= value; OnPropertyChanged("SelectedResult"); }
}
And bind this proprety on your ListView:
<ListView Name="lstView" ItemsSource="{Binding Path=SimResults}" SelectedItem="{Binding SelectedResult}">

Just make sure you initialize selectedItemCommand before InitializeComponent() in the ctor, eg:
public MainWindow()
{
this.selectedItemCommand = new RelayCommand(this.SelectedItems);
InitializeComponent();
}

Related

Problems with Binding Checkboxes in a Listview in xaml Code

What i have:
i have a ListView with 6 Values(string) and a Name(string). I also added a Checkbox for each row.
enter image description here
Struct:
public struct ststuff
{
public bool bEnable { get; set; }
public string sone { get; set; }
public string stwo { get; set; }
public string stree { get; set; }
public string sa { get; set; }
public string sb { get; set; }
public string sc { get; set; }
public string sName { get; set; }
}
Collection:
private ObservableCollection<ststuff> _astuff;
Some Code:
<ListView Margin="10,100,10,10" Name="lvstuff" ItemsSource="{Binding astuff,Mode=TwoWay,ElementName=window}" SelectedIndex="{Binding nIndex,Mode=TwoWay,ElementName=window}">
<ListView.View>
<GridView>
<GridViewColumn Header="[]">
<GridViewColumn.CellTemplate>
<DataTemplate>
<CheckBox HorizontalAlignment="Center" VerticalAlignment="Center" IsChecked="{Binding bEnable,ElementName=window}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Name" Width="150" DisplayMemberBinding="{Binding sName}"/>
<GridViewColumn Header="a" Width="100" DisplayMemberBinding="{Binding sa}" />
<GridViewColumn Header="b" Width="100" DisplayMemberBinding="{Binding sb}" />
<GridViewColumn Header="c" Width="100" DisplayMemberBinding="{Binding sc}" />
<GridViewColumn Header="1" Width="100" DisplayMemberBinding="{Binding sone}" />
<GridViewColumn Header="2" Width="100" DisplayMemberBinding="{Binding stwo}" />
<GridViewColumn Header="3" Width="100" DisplayMemberBinding="{Binding stree}" />
</GridView>
</ListView.View>
</ListView>
Now what i want:
Click on one check box and save true/false in a a value.(e.g. _astuff[0].bEnable for first checkbox)
Atm. if i click on one box all boxes goes check/uncheck because i binded all to the same value.
Somehow i want to bind IsChecked Property of Checkbox to bEnable of my struct but i dont know how!
I got it maybe not the best way but it works.
private void CheckBox_Checked(object sender, RoutedEventArgs e)
{
CheckBox cb = e.OriginalSource as CheckBox;
int index = _astuff.IndexOf((ststuff)(cb).DataContext);
ststuff stuff= _astuff[index];
stuff.bEnable = true;
_astuff[index] = stuff;
}
The problem is that the selected index of ListView dont change by check/uncheck a checkbox. In this way you can find the index in your collection. You have to do the same with Uncheck event.

How to align text or content in GridView (ListView) columns

I want to declare GridViewColumn in ListView this way:
<ListView ItemsSource="{Binding Sells}">
<ListView.View>
<GridView>
<GridViewColumn Header="No. of order" Width="80"
DisplayMemberBinding="{Binding NoOfOrder, StringFormat=N0}"/>
<GridViewColumn Header="Quantity" Width="100"
DisplayMemberBinding="{Binding Quantity, StringFormat=N0}"/>
<GridViewColumn Header="Price" Width="100"
DisplayMemberBinding="{Binding Price, StringFormat=N2}"/>
</GridView>
</ListView.View>
</ListView>
But the problem with this is the column headers as well as the cells don't align in the way I want them to, I've several ListView in my app and I want some of their column headers as well as cells to be aligned center and others to be right and the solution I've found on this site is to do something like this:
<ListView ItemsSource="{Binding Buys}">
<ListView.Resources>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListView.Resources>
<ListView.View>
<GridView>
<GridViewColumn Width="80">
<GridViewColumn.Header>
<GridViewColumnHeader Content="No. of order" HorizontalContentAlignment="Center"/>
</GridViewColumn.Header>
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding NoOfOrder}" TextAlignment="Center"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Width="100">
<GridViewColumn.Header>
<GridViewColumnHeader Content="Quantity" HorizontalContentAlignment="Right"/>
</GridViewColumn.Header>
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Quantity, StringFormat=N0}" TextAlignment="Right"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Width="100">
<GridViewColumn.Header>
<GridViewColumnHeader Content="Price" HorizontalContentAlignment="Right"/>
</GridViewColumn.Header>
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Price, StringFormat=N2}" TextAlignment="Right"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
That's a lot for a 3 column ListView! I've some other that have 7/8 columns so it definitely will be a pain to rewrite all of them like this! Is there a way to declare these ListView in the way I defined in the first example for Sells and have some templating and binding mechanism so that I can write those columns this way:
<ListView ItemsSource="{Binding Sells}">
<ListView.View>
<GridView>
<GridViewColumn Header="No. of order" Align="Center" Width="80"
DisplayMemberBinding="{Binding NoOfOrder, StringFormat=N0}"/>
<GridViewColumn Header="Quantity" Align="Right" Width="100"
DisplayMemberBinding="{Binding Quantity, StringFormat=N0}"/>
<GridViewColumn Header="Price" Align="Right" Width="100"
DisplayMemberBinding="{Binding Price, StringFormat=N2}"/>
</GridView>
</ListView.View>
</ListView>
or something similar?
A simple solution is to write an Attached Behavior. This implementation uses two properties. ListView.IsColumnContentAlignmentEnabled must be set on the ListView and ListView.ColumnContentAlignment of type TextAlignment on the corresponding GridViewColumn (if this property is not set the value will default to TextAlignment.Left).
This is because there is no easy way to get the parent ListView of a GridViewColumn.
The following Attached Behavior allows to align any FrameworkElement (not only TextBlock elements):
ListView.cs
// Attached Behavior
public class ListView : DependencyObject
{
#region IsColumnContentAlignmentEnabled attached property
public static readonly DependencyProperty IsColumnContentAlignmentEnabledProperty = DependencyProperty.RegisterAttached(
"IsColumnContentAlignmentEnabled", typeof(bool), typeof(ListView), new PropertyMetadata(false, ListView.OnIsColumnContentAlignmentEnabledChanged));
public static void SetIsColumnContentAlignmentEnabled(DependencyObject attachingElement, bool value) => attachingElement.SetValue(ListView.IsColumnContentAlignmentEnabledProperty, value);
public static bool GetIsColumnContentAlignmentEnabled(DependencyObject attachingElement) => (bool) attachingElement.GetValue(ListView.IsColumnContentAlignmentEnabledProperty);
#endregion
#region ColumnContentAlignment attached property
public static readonly DependencyProperty ColumnContentAlignmentProperty = DependencyProperty.RegisterAttached(
"ColumnContentAlignment", typeof(TextAlignment), typeof(ListView), new PropertyMetadata(TextAlignment.Left, ListView.OnColumnContentAlignmentChanged));
public static void SetColumnContentAlignment(DependencyObject attachingElement, TextAlignment value) => attachingElement.SetValue(ListView.ColumnContentAlignmentProperty, value);
public static TextAlignment GetColumnContentAlignment(DependencyObject attachingElement) => (TextAlignment) attachingElement.GetValue(ListView.ColumnContentAlignmentProperty);
#endregion
private static Dictionary<GridViewColumn, System.Windows.Controls.ListView> ColumnListViewTable { get; }
static ListView()
{
ListView.ColumnListViewTable = new Dictionary<GridViewColumn, System.Windows.Controls.ListView>();
}
private static void OnColumnContentAlignmentChanged(DependencyObject attachingElement, DependencyPropertyChangedEventArgs e)
{
// Get the parent ListView of the current column
if (attachingElement is GridViewColumn gridViewColumn
&& ListView.ColumnListViewTable.TryGetValue(gridViewColumn, out System.Windows.Controls.ListView listView))
{
ListView.AlignColumns(listView);
}
}
private static void OnIsColumnContentAlignmentEnabledChanged(DependencyObject attachingElement, DependencyPropertyChangedEventArgs e)
{
if (!(attachingElement is System.Windows.Controls.ListView listView))
{
return;
}
bool isEnabled = (bool) e.NewValue;
if (isEnabled)
{
if (!listView.IsLoaded)
{
listView.Loaded += ListView.InitializeColumnsOnListViewLoaded;
return;
}
ListView.InitializeColumns(listView);
}
else
{
ListView.UnregisterColumns(listView);
}
}
private static void InitializeColumnsOnListViewLoaded(object sender, RoutedEventArgs e)
{
var listView = sender as System.Windows.Controls.ListView;
listView.Loaded -= ListView.InitializeColumnsOnListViewLoaded;
ListView.InitializeColumns(listView);
}
private static void InitializeColumns(System.Windows.Controls.ListView listView)
{
if (ListView.TryRegisterColumns(listView))
{
ListView.AlignColumns(listView);
}
}
private static void AlignColumns(System.Windows.Controls.ListView listView)
{
IEnumerable<ListBoxItem> listViewItemContainers = listView.Items
.OfType<object>()
.Select(item => listView.ItemContainerGenerator.ContainerFromItem(item) as ListBoxItem);
foreach (ListBoxItem listViewItemContainer in listViewItemContainers)
{
if (!listViewItemContainer.TryFindVisualChildElement(out GridViewRowPresenter rowPresenter))
{
continue;
}
List<object> rowElements = LogicalTreeHelper.GetChildren(rowPresenter)
.Cast<object>()
.ToList();
for (int columnIndex = 0; columnIndex < rowPresenter.Columns.Count; columnIndex++)
{
TextAlignment columnContentAlignment = ListView.GetColumnContentAlignment(rowPresenter.Columns.ElementAt(columnIndex));
if (rowElements.ElementAt(columnIndex) is TextBlock textBlock)
{
textBlock.TextAlignment = columnContentAlignment;
}
// Because the TextBlock's width is set to Auto
// the aligned text will always appear to be left aligned.
// Therefore set equivalent HorizontalAlignment to compensate.
// Also setting the HorizontalAlignment enables alignment of all FrameworkElements
if (rowElements.ElementAt(columnIndex) is FrameworkElement frameworkElement)
{
frameworkElement.HorizontalAlignment = ListView.GetHorizontalAlignment(columnContentAlignment);
}
}
}
}
private static HorizontalAlignment GetHorizontalAlignment(TextAlignment textBlockTextAlignment)
{
switch (textBlockTextAlignment)
{
case TextAlignment.Center: return HorizontalAlignment.Center;
case TextAlignment.Right: return HorizontalAlignment.Right;
case TextAlignment.Justify: return HorizontalAlignment.Stretch;
default: return HorizontalAlignment.Left;
}
}
private static void UnregisterColumns(System.Windows.Controls.ListView listView)
{
if (!(listView.View is System.Windows.Controls.GridView gridView))
{
return;
}
gridView.Columns
.ToList()
.ForEach(column => ListView.ColumnListViewTable.Remove(column));
}
private static bool TryRegisterColumns(System.Windows.Controls.ListView listView)
{
if (!(listView.View is System.Windows.Controls.GridView gridView))
{
return false;
}
gridView.Columns
.ToList()
.ForEach(column => ListView.ColumnListViewTable.Add(column, listView));
return true;
}
}
Usage Example
<ListView ListView.IsColumnContentAlignmentEnabled="True"
ItemsSource="{Binding Persons}">
<ListView.View>
<GridView>
<GridViewColumn DisplayMemberBinding="{Binding FirstName}"
ListView.ColumnContentAlignment="Right"
Header="First Name" />
</GridViewColumn>
<GridViewColumn DisplayMemberBinding="{Binding LastName}"
ListView.ColumnContentAlignment="Center"
Header="Last Name" />
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>

How to delete row in listview MVVM?

I have 1 list. Each row of the list will have a delete button. I want to delete that line when I click that button and must use mvvm.
<ListView x:Name="ListViewRoutePlan" SelectedIndex="0" ItemsSource="{Binding RoutePlanResource}" Style="{StaticResource MaterialDesignListView}">
<ListView.View>
<GridView>
<GridViewColumn DisplayMemberBinding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListViewItem}},Converter={StaticResource IndexConverter}}" Header="STT" />
<GridViewColumn DisplayMemberBinding="{Binding Prioritize}" Header="Prioritize" />
<GridViewColumn DisplayMemberBinding="{Binding PlanStatus}" Header="Plan Status" />
<GridViewColumn DisplayMemberBinding="{Binding Note}" Header="Note" />
<GridViewColumn Header="Delete" >
<GridViewColumn.CellTemplate>
<DataTemplate>
<Button Command="{Binding ElementName=ListViewRoutePlan,Path=DataContext.RemoveSubjectCommand}" CommandParameter="{Binding ElementName=ListViewRoutePlan}" Background="{x:Null}" BorderBrush="{x:Null}" Margin="0,-5,0,0" HorizontalAlignment="Left">
<materialDesign:PackIcon Kind="Delete" Margin="-5,0,0,0" Foreground="Black" Width="20" Height="20" />
</Button>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
view model
I have run it here but I do not know how to write processing code to delete
public class viewmodel : BaseViewModel
{
public ICommand RemoveSubjectCommand { get; set; }
public viewmodel()
{
RemoveSubjectCommand = new RelayCommand<ListView>((p) => { return true; }, (OnEdit));
}
private void OnEdit(ListView lsv)
{
}
}
The button command parameter should be the current item bound to the cell
CommandParameter="{Binding}"
And the command should be updated accordingly to remove the selected item from the collection
public class viewmodel : BaseViewModel {
public viewmodel() {
RemoveSubjectCommand = new RelayCommand<MyItemModel>((p) => { return true; }, (OnRemoveSubject));
//assuming RoutePlanResource initialized and populated
}
public ObservableCollection<MyItemModel> RoutePlanResource {
//assuming boilerplate getter and setter with notification
}
public ICommand RemoveSubjectCommand { get; set; }
private void OnRemoveSubject(MyItemModel item) {
RoutePlanResource.Remove(item);
//...
}
}

Unable to Bind ObservableCollection from MVVM to ListView in WPF

I am trying to bind the List View to Observable Collection of a class and for some reason, the DisplayMemberBinding attribute of GridViewColumn is not binding to the content of collection.
If I use ListView.ItemTemplate, everything works fine. But I need the data in the form of a grid, so I am using GridView inside ListView.View.
First Image, Image 1 is the AssessmentSummaryList content in debug mode.
Second Image, Image 2 is the final output shown on the screen. The content from the list is not binding to the GridViewColumn.
These are the two view models I am using:
public class AssessmentSummaryViewModel : BaseViewModel
{
public string Question { get; set; }
public string Title { get; set; }
public string SelectedOption { get; set; }
public int Id { get; set; }
}
public class AssessmentViewModel : BaseViewModel
{
private ObservableCollection<AssessmentSummaryViewModel> assessmentSummaryList;
public ObservableCollection<AssessmentSummaryViewModel> AssessmentSummaryList
{
get { return assessmentSummaryList; }
set
{
assessmentSummaryList = value;
NotifyPropertyChanged("AssessmentSummaryList");
}
}
public void SetNextAssessment()
{
AssessmentSummaryList= Service.GetAssessmentSummary(ApplicationModel.SelectedModule.Id,
ApplicationModel.SelectedUtility.Id); //the service returns ObservableCollection<AssessmentSummaryViewModel> data
var EndScreen = new AssessmentSummaryView(); //Setting the last screen to AssessmentSumamryView, this will be called dynamically from other UserControl Xaml Page
}
}
AssessmentSummaryView Xaml Code is as follows:
<ListView x:Name="lstvSummary1" Grid.Row="2" ItemsSource="{Binding Path=DataContext.AssessmentSummaryList, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}">
<ListView.View>
<GridView ColumnHeaderContainerStyle="{StaticResource myHeaderStyle}">
<GridViewColumn DisplayMemberBinding="{Binding Path=Title}" Width="200">
<GridViewColumnHeader Content="Design Element" />
</GridViewColumn>
<GridViewColumn DisplayMemberBinding="{Binding Path=Question}" Width="400">
<GridViewColumnHeader Content="Question" />
</GridViewColumn>
<GridViewColumn DisplayMemberBinding="{Binding Path=SelectedOption}" Width="300">
<GridViewColumnHeader Content="Response" />
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
AssessmentSummaryView Code-behind is as follows:
public partial class AssessmentSummaryView : UserControl
{
public AssessmentSummaryView()
{
InitializeComponent();
}
}
Try something like:
<ListView ItemsSource="{Binding Path=DataContext.AssessmentSummaryList, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}">
<ListView.View>
<GridView>
<GridView.Columns>
<GridViewColumn Header="Design Element" Width="200" DisplayMemberBinding="{Binding Path=Title}"/>
<GridViewColumn Header="Question" Width="400" DisplayMemberBinding="{Binding Path=Question}"/>
<GridViewColumn Header="Response" Width="300" DisplayMemberBinding="{Binding Path=SelectedOption}"/>
</GridView.Columns>
</GridView>
</ListView.View>

How to bind a ListView to multiple collections stored in a single ViewModel in WPF?

I have 2 collections I want to bind to a separate GridViewColumn in a ListView:
public class EffectView : INotifyPropertyChanged
{
ObservableCollection<Effect> effects;
public ObservableCollection<Effect> Effects
{
get { return this.effects; }
set
{
this.effects = value;
this.RaisePropertyChanged ( "Effects" );
}
}
ObservableCollection<EffectDescription> descriptions;
public ObservableCollection<EffectDescription> Descriptions
{
get { return this.descriptions; }
set
{
this.descriptions = value;
this.RaisePropertyChanged ( "Descriptions" );
}
}
}
I can do this:
<ListView ItemsSource="{Binding EffectView.Effects}">
<ListView.View>
<GridView>
<GridViewColumn Width="Auto"
DisplayMemberBinding="{Binding Name}"
Header="Name" />
<GridViewColumn Width="Auto"
DisplayMemberBinding="{Binding Opacity}"
Header="Opacity" />
<GridViewColumn Width="Auto"
DisplayMemberBinding="{Binding ?}"
Header="Description" />
</GridView>
</ListView.View>
</ListView>
But then everything is scoped to EffectView.Effects, but I want the default scope to be EffectView so I can easily assign multiple collections to the ListView.
Something like:
<ListView ItemsSource="{Binding EffectView}">
<ListView.View>
<GridView>
<GridViewColumn Width="Auto"
DisplayMemberBinding="{Binding Effects Path=Name}"
Header="Name" />
<GridViewColumn Width="Auto"
DisplayMemberBinding="{Binding Effects Path=Opacity}"
Header="Opacity" />
<GridViewColumn Width="Auto"
DisplayMemberBinding="{Binding Descriptions Path=Usage}"
Header="Description" />
</GridView>
</ListView.View>
</ListView>
Any way to accomplish this?
The ItemsSource of a ListView is the collection whose items will appear in the list. So think for a moment about what you're asking the ListView to do:
As a source collection, use something that isn't a collection, but contains them
For each row in the list, display an item from one collection, and an item from the other collection
Pay attention to that second point: every row must display some item from the Events collection and some item from the Descriptions collection.
Which item shall it pick from each? What's the relationship between the items in the two collections?
It appears that what you really need is a collection of objects that contains both an Event and a Description. You can then bind to that collection to display elements of both entities. Roughly, something like this:
public class EffectView : INotifyPropertyChanged
{
ObservableCollection<EffectsAndDescriptions> effects;
public ObservableCollection<EffectAndDescriptions> Effects
{
get { return this.effects; }
set
{
this.effects = value;
this.RaisePropertyChanged ( "EffectsAndDescriptions" );
}
}
}
internal class EffectsAndDescriptions
{
public Effect Effect { get; set; }
public Description Description { get; set; }
}
Now you can bind to the EffectsAndDescriptions collection (note this assumes the DataContext of the ListView's parent is EffectView)
<ListView ItemsSource="{Binding EffectsAndDescriptions}">
<ListView.View>
<GridView>
<GridViewColumn Width="Auto"
DisplayMemberBinding="{Binding Effect.Name}"
Header="Name" />
<GridViewColumn Width="Auto"
DisplayMemberBinding="{Binding Effect.Opacity}"
Header="Opacity" />
<GridViewColumn Width="Auto"
DisplayMemberBinding="{Binding Description.Usage}"
Header="Description" />
</GridView>
</ListView.View>
</ListView>
That's not really possible. ItemsSource expects to be passed something that, at a minimum, implements IEnumerable. Even if you implemented IEnumerable on your EffectView, you would need to wrap Effect and EffectDescription into a single object to return.
You could bind your ListView to your Effects collection, then use a custom IValueConverter to take the Effect and return a description. But I'm not sure this would fit your situation.
Your best bet would be to include a wrapper object (i.e. EffectWithDescription) that includes both your Effect and your EffectDescription. Then expose a collection of EffectWithDescription objects. You'd need to ensure that this new collection is keep in sync with the other collections though.

Categories

Resources