0

I have a big dataset,filtered by user criteria with a ICollectionView and I would like to perform data pagination from qualified result.

I supose that I need to ICollectionView one for filtering data and other for pagination. Is this the right way?

The problem is that ICollectionView. Source can't be an other ICollectionView. I've tried some other alternatives but without success.

IEnumerable<foo> data;
ICollectionView MyDataFilter;
...
this.MyDataFilter = CollectionViewSource.GetDefaultView(data);

System.Diagnostics.Debug.Assert(this.MyDataFilter.CanFilter);
this.MyDataFilter.Filter = DataViewFilter;

PaginationView = CollectionViewSource.GetDefaultView(this.MyDataFilter);
this.PaginationView.Filter = PaginationViewFilter;
arturn
  • 725
  • 2
  • 11
  • 25
  • I assume your are paging similar to this http://stackoverflow.com/questions/784726/how-can-i-paginate-a-wpf-datagrid in which case you should just need to change the `this._innerList.Count` to count where passes filter, then you don't need to stack your views – MikeT Feb 21 '17 at 16:24
  • See the example below – MikeT Feb 22 '17 at 14:33
  • I'm evaluating if this example is suitable for my case. Thanks a lot! I will give some feedback! – arturn Feb 22 '17 at 16:08
  • you'll need to improve the code yourself, for example if your on page 10 and change the filter so there are only 5 pages the example will crash – MikeT Feb 22 '17 at 16:14
  • Yes, I know that I must make changes, but now I'm only evaluating the right way to solve my issue.. – arturn Feb 23 '17 at 08:07

1 Answers1

0

using the example from How can I paginate a WPF DataGrid?

and simply changing the criteria to take into account the filtering

EG

public class PagingCollectionView : CollectionView
{
    private readonly int _itemsPerPage;

    private int _currentPage = 1;

    public PagingCollectionView(IEnumerable innerList, int itemsPerPage)
        : base(innerList)
    {
        this._itemsPerPage = itemsPerPage;
    }

    public int FilteredCount
    {
        get {return FilteredCollection.Count(); }
    }

    private IEnumerable<object> FilteredCollection => this.SourceCollection.OfType<object>().Where(o=>Filter(o));
    public override int Count
    {
        get
        {
            if (FilteredCount == 0) return 0;
            if (this._currentPage < this.PageCount) // page 1..n-1
            {
                return this._itemsPerPage;
            }
            else // page n
            {
                var itemsLeft = FilteredCount % this._itemsPerPage;
                if (0 == itemsLeft)
                {
                    return this._itemsPerPage; // exactly itemsPerPage left
                }
                else
                {
                    // return the remaining items
                    return itemsLeft;
                }
            }
        }
    }

    public int CurrentPage
    {
        get { return this._currentPage; }
        set
        {
            this._currentPage = value;
            this.OnPropertyChanged(new PropertyChangedEventArgs("CurrentPage"));
        }
    }

    public int ItemsPerPage { get { return this._itemsPerPage; } }

    public int PageCount
    {
        get
        {
            return (FilteredCount + this._itemsPerPage - 1)
                / this._itemsPerPage;
        }
    }

    private int EndIndex
    {
        get
        {
            var end = this._currentPage * this._itemsPerPage - 1;
            return (end > FilteredCount) ? FilteredCount : end;
        }
    }

    private int StartIndex
    {
        get { return (this._currentPage - 1) * this._itemsPerPage; }
    }

    public override object GetItemAt(int index)
    {
        var offset = index % (this._itemsPerPage);
        return this.FilteredCollection.ElementAt(this.StartIndex + offset);
    }

    public void MoveToNextPage()
    {
        if (this._currentPage < this.PageCount)
        {
            this.CurrentPage += 1;
        }
        this.Refresh();
    }

    public void MoveToPreviousPage()
    {
        if (this._currentPage > 1)
        {
            this.CurrentPage -= 1;
        }
        this.Refresh();
    }
}   

To Demo VM:

public class VM:BindableBase
{
    public VM()
    {
        PagingCollectionView = new PagingCollectionView(
            Enumerable.Range(300, 1000).Select(i => i.ToString("X")),
            5);
        PagingCollectionView.Filter = (o) => string.IsNullOrWhiteSpace(Filter) || o.ToString().StartsWith(Filter);

        Next = new DelegateCommand(PagingCollectionView.MoveToNextPage);
        Previous = new DelegateCommand(PagingCollectionView.MoveToPreviousPage );
    }

    private string _Filter;

    public string Filter
    {
        get { return _Filter; }
        set {
            if(SetProperty(ref _Filter, value))
                PagingCollectionView.Refresh();
        }
    }

    public PagingCollectionView PagingCollectionView { get; set; }

    public DelegateCommand Next { get; set; }
    public DelegateCommand Previous { get; set; }
}

XAML:

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApplication1"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Window.DataContext>
        <local:VM x:Name="vm" />
    </Window.DataContext>

    <DockPanel >
        <TextBox DockPanel.Dock="Top" Text="{Binding Filter}"/>
        <Button DockPanel.Dock="Left" Content="&lt;" Command="{Binding Previous}"  />
        <Button DockPanel.Dock="Right" Content="&gt;" Command="{Binding Next}"/>
        <ListView ItemsSource="{Binding PagingCollectionView}"/>

    </DockPanel>
</Window>

NOTE: this code is not production code just an example to show the process required, it is not robust

Community
  • 1
  • 1
MikeT
  • 5,398
  • 3
  • 27
  • 43
  • The property 'FilteredCollection' is filtering the entire collection every time we access it, Is it so? – arturn Feb 23 '17 at 08:05
  • yes you could improve efficiency by caching the results just make sure you clear the cache on refresh – MikeT Feb 23 '17 at 09:51
  • its also worth noting that the collection view now accepts IEnumerable not ILsit – MikeT Feb 23 '17 at 09:57
  • I get no errors at all with the code above as written, if you have made any changes then thats probably the source – MikeT Feb 24 '17 at 13:35
  • see http://stackoverflow.com/questions/22524569/an-itemscontrol-is-inconsistent-with-its-items-source-wpf-listbox – MikeT Feb 24 '17 at 13:37
  • I'm finally going to discard this solution as I've come up with a solution that might be better. I will write a post when I've finished! – arturn Mar 08 '17 at 15:21