QueryFeaturesAsync: Paging

1128
2
Jump to solution
02-04-2019 09:28 AM
MikeQuetel1
Occasional Contributor

I've got a map service that supports paging and has MaxFeatureCount set to 4000.  Can someone familiar with using runtime's paging capability on QueryFeaturesAsync() give me some pointers on how I can return more than 4000 features?  I'm seeing properties on the QueryParameters object such as MaxFeatures, MaxAllowableOffset and ResultOffset but the docs don't really indicate how these should be utilized.  Do I need to execute my query in a Do While loop?  If so does that mean I need to first do a QueryFeatureCountAsync() to set up the paging logic?

Thanks in advance!

Tags (2)
0 Kudos
1 Solution

Accepted Solutions
JenniferNery
Esri Regular Contributor

Here's an example of using ResultOffset and MaxFeatures. The table need to support pagination for you to do this. Yes, you will need to know total count using QueryFeaturesCountAsync and determine how many features you want a page to calculate the number of pages. This will let you compute the appropriate ResultOffset.

public MainWindow()
{
    InitializeComponent();
    _parameters = new QueryParameters() { WhereClause = "1=1" };
    _table = new ServiceFeatureTable(new Uri("http://sampleserver6.arcgisonline.com/arcgis/rest/services/Census/MapServer/2"));
    _table.Loaded += (s, e) =>
    {
        var table = (ServiceFeatureTable)s;
        if (table.LoadStatus == LoadStatus.Loaded)
            MessageBox.Show($"SupportsPagination : {table.LayerInfo?.SupportsPagination}");
    };
    RecordsPerPage.ItemsSource = new int[] { 10, 20, 30, 40, 50 };
    RecordsPerPage.SelectedItem = 25;
}

private ServiceFeatureTable _table;
private QueryParameters _parameters;
private int _totalFeatureCount;
private int _currentPageIndex;
private int _recordsPerPage;
private int _totalPages;

private async void OnFirstPageClick(object sender, RoutedEventArgs e)
{
    _currentPageIndex = 0;
    await GetResultForPageAsync();
}

private async void OnPreviousPageClick(object sender, RoutedEventArgs e)
{
    if (_currentPageIndex - 1 < 0)
        return;
    _currentPageIndex--;
    await GetResultForPageAsync();
}

private async void OnNextPageClick(object sender, RoutedEventArgs e)
{
    if (_currentPageIndex + 1 > _totalPages)
        return;
    _currentPageIndex++;
    await GetResultForPageAsync();

}
private async void OnLastPageClick(object sender, RoutedEventArgs e)
{
    _currentPageIndex = _totalPages - 1;
    await GetResultForPageAsync();
}

private async void OnRecordsPerPageChanged(object sender, SelectionChangedEventArgs e)
{
    try
    {
        _currentPageIndex = 0;
        if (_totalFeatureCount == 0)
            _totalFeatureCount = (int)(await _table.QueryFeatureCountAsync(_parameters));

        _recordsPerPage = (int)RecordsPerPage.SelectedItem;
        _totalPages = _totalFeatureCount / _recordsPerPage;

        await GetResultForPageAsync();
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message, ex.GetType().Name);
    }
}

private async Task GetResultForPageAsync()
{
    try
    {
        Result.ItemsSource = Enumerable.Empty<ArcGISFeature>();
        CurrentPageNumber.Text = $"{_currentPageIndex + 1}";
        TotalPages.Text = $"{_totalPages}";

        _parameters.ResultOffset = _currentPageIndex * _recordsPerPage;
        _parameters.MaxFeatures = _recordsPerPage;
        var result = await _table.QueryFeaturesAsync(_parameters, QueryFeatureFields.LoadAll);
        Result.ItemsSource = result.ToArray();
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message, ex.GetType().Name);
    }
}
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="*" />
        <RowDefinition Height="Auto" />
    </Grid.RowDefinitions>
    <DataGrid x:Name="Result"
              AutoGenerateColumns="False">
        <DataGrid.Columns>
            <DataGridTextColumn Binding="{Binding Attributes[STATE_NAME]}"
                                Header="State Name" />
            <DataGridTextColumn Binding="{Binding Attributes[POP2000]}"
                                Header="Population 2000" />
            <DataGridTextColumn Binding="{Binding Attributes[POP2007]}"
                                Header="Population 2007" />
            <DataGridTextColumn Binding="{Binding Attributes[HOUSEHOLDS]}"
                                Header="Households" />
            <DataGridTextColumn Binding="{Binding Attributes[FAMILIES]}"
                                Header="Families" />
        </DataGrid.Columns>
    </DataGrid>
    <Grid Grid.Row="1">
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition />
            <ColumnDefinition />
            <ColumnDefinition />
            <ColumnDefinition />
            <ColumnDefinition />
            <ColumnDefinition />
            <ColumnDefinition />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <Button Content="|&lt;&lt;"
                Click="OnFirstPageClick" />
        <Button Grid.Column="1"
                Content="&lt;"
                Click="OnPreviousPageClick" />
        <Button Grid.Column="2"
                Content="&gt;"
                Click="OnNextPageClick" />
        <Button Grid.Column="3"
                Content="&gt;&gt;|"
                Click="OnLastPageClick" />
        <TextBlock  Grid.Column="4"
                    x:Name="CurrentPageNumber" />
        <TextBlock  Grid.Column="5"
                    Text=" of " />
        <TextBlock  Grid.Column="6"
                    x:Name="TotalPages" />
        <TextBlock  Grid.Column="7"
                    Text="Show : " />
        <ComboBox  Grid.Column="8"
                   SelectionChanged="OnRecordsPerPageChanged"
                   x:Name="RecordsPerPage" />
    </Grid>
</Grid>

View solution in original post

2 Replies
JenniferNery
Esri Regular Contributor

Here's an example of using ResultOffset and MaxFeatures. The table need to support pagination for you to do this. Yes, you will need to know total count using QueryFeaturesCountAsync and determine how many features you want a page to calculate the number of pages. This will let you compute the appropriate ResultOffset.

public MainWindow()
{
    InitializeComponent();
    _parameters = new QueryParameters() { WhereClause = "1=1" };
    _table = new ServiceFeatureTable(new Uri("http://sampleserver6.arcgisonline.com/arcgis/rest/services/Census/MapServer/2"));
    _table.Loaded += (s, e) =>
    {
        var table = (ServiceFeatureTable)s;
        if (table.LoadStatus == LoadStatus.Loaded)
            MessageBox.Show($"SupportsPagination : {table.LayerInfo?.SupportsPagination}");
    };
    RecordsPerPage.ItemsSource = new int[] { 10, 20, 30, 40, 50 };
    RecordsPerPage.SelectedItem = 25;
}

private ServiceFeatureTable _table;
private QueryParameters _parameters;
private int _totalFeatureCount;
private int _currentPageIndex;
private int _recordsPerPage;
private int _totalPages;

private async void OnFirstPageClick(object sender, RoutedEventArgs e)
{
    _currentPageIndex = 0;
    await GetResultForPageAsync();
}

private async void OnPreviousPageClick(object sender, RoutedEventArgs e)
{
    if (_currentPageIndex - 1 < 0)
        return;
    _currentPageIndex--;
    await GetResultForPageAsync();
}

private async void OnNextPageClick(object sender, RoutedEventArgs e)
{
    if (_currentPageIndex + 1 > _totalPages)
        return;
    _currentPageIndex++;
    await GetResultForPageAsync();

}
private async void OnLastPageClick(object sender, RoutedEventArgs e)
{
    _currentPageIndex = _totalPages - 1;
    await GetResultForPageAsync();
}

private async void OnRecordsPerPageChanged(object sender, SelectionChangedEventArgs e)
{
    try
    {
        _currentPageIndex = 0;
        if (_totalFeatureCount == 0)
            _totalFeatureCount = (int)(await _table.QueryFeatureCountAsync(_parameters));

        _recordsPerPage = (int)RecordsPerPage.SelectedItem;
        _totalPages = _totalFeatureCount / _recordsPerPage;

        await GetResultForPageAsync();
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message, ex.GetType().Name);
    }
}

private async Task GetResultForPageAsync()
{
    try
    {
        Result.ItemsSource = Enumerable.Empty<ArcGISFeature>();
        CurrentPageNumber.Text = $"{_currentPageIndex + 1}";
        TotalPages.Text = $"{_totalPages}";

        _parameters.ResultOffset = _currentPageIndex * _recordsPerPage;
        _parameters.MaxFeatures = _recordsPerPage;
        var result = await _table.QueryFeaturesAsync(_parameters, QueryFeatureFields.LoadAll);
        Result.ItemsSource = result.ToArray();
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message, ex.GetType().Name);
    }
}
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="*" />
        <RowDefinition Height="Auto" />
    </Grid.RowDefinitions>
    <DataGrid x:Name="Result"
              AutoGenerateColumns="False">
        <DataGrid.Columns>
            <DataGridTextColumn Binding="{Binding Attributes[STATE_NAME]}"
                                Header="State Name" />
            <DataGridTextColumn Binding="{Binding Attributes[POP2000]}"
                                Header="Population 2000" />
            <DataGridTextColumn Binding="{Binding Attributes[POP2007]}"
                                Header="Population 2007" />
            <DataGridTextColumn Binding="{Binding Attributes[HOUSEHOLDS]}"
                                Header="Households" />
            <DataGridTextColumn Binding="{Binding Attributes[FAMILIES]}"
                                Header="Families" />
        </DataGrid.Columns>
    </DataGrid>
    <Grid Grid.Row="1">
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition />
            <ColumnDefinition />
            <ColumnDefinition />
            <ColumnDefinition />
            <ColumnDefinition />
            <ColumnDefinition />
            <ColumnDefinition />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <Button Content="|&lt;&lt;"
                Click="OnFirstPageClick" />
        <Button Grid.Column="1"
                Content="&lt;"
                Click="OnPreviousPageClick" />
        <Button Grid.Column="2"
                Content="&gt;"
                Click="OnNextPageClick" />
        <Button Grid.Column="3"
                Content="&gt;&gt;|"
                Click="OnLastPageClick" />
        <TextBlock  Grid.Column="4"
                    x:Name="CurrentPageNumber" />
        <TextBlock  Grid.Column="5"
                    Text=" of " />
        <TextBlock  Grid.Column="6"
                    x:Name="TotalPages" />
        <TextBlock  Grid.Column="7"
                    Text="Show : " />
        <ComboBox  Grid.Column="8"
                   SelectionChanged="OnRecordsPerPageChanged"
                   x:Name="RecordsPerPage" />
    </Grid>
</Grid>
MikeQuetel1
Occasional Contributor

Thanks Jennifer Nery, that looks exactly like what I needed.  Much appreciated!

0 Kudos