Click or drag to resize

GenerateLayerOptionQueryOption Property

Gets or sets a value indicating whether all, filtered or no features will be copied from the server when creating a geodatabase.

Namespace:  Esri.ArcGISRuntime.Tasks.Offline
Assembly:  Esri.ArcGISRuntime (in Esri.ArcGISRuntime.dll) Version: 100.11.0
Syntax
public GenerateLayerQueryOption QueryOption { get; set; }

Return Value

Type: GenerateLayerQueryOption
Returns all, filtered or no features will be copied from the server when creating a geodatabase.
Examples

UWP

Example Name: GenerateOfflineMapWithOverrides

Take a web map offline with additional options for each layer.

Code example screen shot.

C#
// Copyright 2019 Esri.
// 
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
// You may obtain a copy of the License at: http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an 
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific 
// language governing permissions and limitations under the License.

using Esri.ArcGISRuntime.Data;
using Esri.ArcGISRuntime.Geometry;
using Esri.ArcGISRuntime.Mapping;
using Esri.ArcGISRuntime.Symbology;
using Esri.ArcGISRuntime.Tasks;
using Esri.ArcGISRuntime.Tasks.Offline;
using Esri.ArcGISRuntime.UI;
using Esri.ArcGISRuntime.ArcGISServices;
using Esri.ArcGISRuntime.UI.Controls;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Windows.UI.Popups;
using Windows.UI.Xaml.Controls;
using Esri.ArcGISRuntime.Portal;
using Esri.ArcGISRuntime.Security;

namespace ArcGISRuntime.UWP.Samples.GenerateOfflineMapWithOverrides
{
    [ArcGISRuntime.Samples.Shared.Attributes.Sample(
        name: "Generate offline map (overrides)",
        category: "Map",
        description: "Take a web map offline with additional options for each layer.",
        instructions: "Modify the overrides parameters:",
        tags: new[] { "LOD", "adjust", "download", "extent", "filter", "offline", "override", "parameters", "reduce", "scale range", "setting" })]
    public partial class GenerateOfflineMapWithOverrides
    {
        // The job to generate an offline map.
        private GenerateOfflineMapJob _generateOfflineMapJob;

        // The extent of the data to take offline.
        private Envelope _areaOfInterest = new Envelope(-88.1541, 41.7690, -88.1471, 41.7720, SpatialReferences.Wgs84);

        // The ID for a web map item hosted on the server (water network map of Naperville IL).
        private const string WebMapId = "acc027394bc84c2fb04d1ed317aac674";

        public GenerateOfflineMapWithOverrides()
        {
            InitializeComponent();

            // Load the web map, show area of interest, restrict map interaction, and set up authorization. 
            Initialize();
        }

        private async void Initialize()
        {
            try
            {
                // Call a function to set up the AuthenticationManager for OAuth.
                SetOAuthInfo();

                // Create the ArcGIS Online portal.
                ArcGISPortal portal = await ArcGISPortal.CreateAsync();

                // Get the Naperville water web map item using its ID.
                PortalItem webmapItem = await PortalItem.CreateAsync(portal, WebMapId);

                // Create a map from the web map item.
                Map onlineMap = new Map(webmapItem);

                // Display the map in the MapView.
                MyMapView.Map = onlineMap;

                // Disable user interactions on the map (no panning or zooming from the initial extent).
                MyMapView.InteractionOptions = new MapViewInteractionOptions
                {
                    IsEnabled = false
                };

                // Create a graphics overlay for the extent graphic and apply a renderer.
                SimpleLineSymbol aoiOutlineSymbol = new SimpleLineSymbol(SimpleLineSymbolStyle.Solid, System.Drawing.Color.Red, 3);
                GraphicsOverlay extentOverlay = new GraphicsOverlay
                {
                    Renderer = new SimpleRenderer(aoiOutlineSymbol)
                };
                MyMapView.GraphicsOverlays.Add(extentOverlay);

                // Add a graphic to show the area of interest (extent) that will be taken offline.
                Graphic aoiGraphic = new Graphic(_areaOfInterest);
                extentOverlay.Graphics.Add(aoiGraphic);

                // Hide the map loading progress indicator.
                LoadingIndicator.Visibility = Windows.UI.Xaml.Visibility.Collapsed;

                // Clean up any existing output data folders that might exist from running this sample previously. 
                // The output data folder is where the results of the taking the web map offline get stored on the device.
                MyMapView.Unloaded += (s, e) =>
                {
                    // Find output mobile map folders in the temp directory.
                    string[] outputFolders = Directory.GetDirectories(Environment.ExpandEnvironmentVariables("%TEMP%"), "NapervilleWaterNetwork*");

                    // Loop through the folder names and delete them.
                    foreach (string dir in outputFolders)
                    {
                        try
                        {
                            // Delete the folder.
                            Directory.Delete(dir, true);
                        }
                        catch (Exception)
                        {
                            // Ignore exceptions (files might be locked, for example).
                        }
                    }
                };
            }
            catch (Exception ex)
            {
                MessageDialog dialog = new MessageDialog(ex.ToString(), "Error loading map");
                await dialog.ShowAsync();
            }
        }

        private async void TakeMapOfflineButton_Click(object sender, Windows.UI.Xaml.RoutedEventArgs e)
        {
            // Create a new folder for the output mobile map.
            string packagePath = Path.Combine(Environment.ExpandEnvironmentVariables("%TEMP%"), @"NapervilleWaterNetwork");
            int num = 1;
            while (Directory.Exists(packagePath))
            {
                packagePath = Path.Combine(Environment.ExpandEnvironmentVariables("%TEMP%"), @"NapervilleWaterNetwork" + num.ToString());
                num++;
            }

            // Create the output directory.
            Directory.CreateDirectory(packagePath);

            try
            {
                // Show the progress indicator while the job is running.
                BusyIndicator.Visibility = Windows.UI.Xaml.Visibility.Visible;

                // Create an offline map task with the current (online) map.
                OfflineMapTask takeMapOfflineTask = await OfflineMapTask.CreateAsync(MyMapView.Map);

                // Create the default parameters for the task, pass in the area of interest.
                GenerateOfflineMapParameters parameters = await takeMapOfflineTask.CreateDefaultGenerateOfflineMapParametersAsync(_areaOfInterest);

                #region overrides

                // Generate parameter overrides for more in-depth control of the job.
                GenerateOfflineMapParameterOverrides overrides = await takeMapOfflineTask.CreateGenerateOfflineMapParameterOverridesAsync(parameters);

                // Configure the overrides using helper methods.
                ConfigureTileLayerOverrides(overrides);
                ConfigureLayerExclusion(overrides);
                CropWaterPipes(overrides);
                ApplyFeatureFilter(overrides);

                // Create the job with the parameters and output location.
                _generateOfflineMapJob = takeMapOfflineTask.GenerateOfflineMap(parameters, packagePath, overrides);

                #endregion overrides

                // Handle the progress changed event for the job.
                _generateOfflineMapJob.ProgressChanged += OfflineMapJob_ProgressChanged;

                // Await the job to generate geodatabases, export tile packages, and create the mobile map package.
                GenerateOfflineMapResult results = await _generateOfflineMapJob.GetResultAsync();

                // Check for job failure (writing the output was denied, e.g.).
                if (_generateOfflineMapJob.Status != JobStatus.Succeeded)
                {
                    MessageDialog dialog = new MessageDialog("Generate offline map package failed.", "Job status");
                    await dialog.ShowAsync();
                    BusyIndicator.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
                }

                // Check for errors with individual layers.
                if (results.LayerErrors.Any())
                {
                    // Build a string to show all layer errors.
                    System.Text.StringBuilder errorBuilder = new System.Text.StringBuilder();
                    foreach (KeyValuePair<Layer, Exception> layerError in results.LayerErrors)
                    {
                        errorBuilder.AppendLine(string.Format("{0} : {1}", layerError.Key.Id, layerError.Value.Message));
                    }

                    // Show layer errors.
                    string errorText = errorBuilder.ToString();
                    MessageDialog dialog = new MessageDialog(errorText, "Layer errors");
                    await dialog.ShowAsync();
                }

                // Display the offline map.
                MyMapView.Map = results.OfflineMap;

                // Apply the original viewpoint for the offline map.
                MyMapView.SetViewpoint(new Viewpoint(_areaOfInterest));

                // Enable map interaction so the user can explore the offline data.
                MyMapView.InteractionOptions.IsEnabled = true;

                // Hide the "Take map offline" button.
                TakeOfflineArea.Visibility = Windows.UI.Xaml.Visibility.Collapsed;

                // Show a message that the map is offline.
                MessageArea.Visibility = Windows.UI.Xaml.Visibility.Visible;
            }
            catch (TaskCanceledException)
            {
                // Generate offline map task was canceled.
                MessageDialog dialog = new MessageDialog("Taking map offline was canceled");
                await dialog.ShowAsync();
            }
            catch (Exception ex)
            {
                // Exception while taking the map offline.
                MessageDialog dialog = new MessageDialog(ex.Message, "Offline map error");
                await dialog.ShowAsync();
            }
            finally
            {
                // Hide the activity indicator when the job is done.
                BusyIndicator.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
            }
        }

        #region overrides

        private void ConfigureTileLayerOverrides(GenerateOfflineMapParameterOverrides overrides)
        {
            // Create a parameter key for the first basemap layer.
            OfflineMapParametersKey basemapKey = new OfflineMapParametersKey(MyMapView.Map.Basemap.BaseLayers.First());

            // Get the export tile cache parameters for the layer key.
            ExportTileCacheParameters basemapParams = overrides.ExportTileCacheParameters[basemapKey];

            // Clear the existing level IDs.
            basemapParams.LevelIds.Clear();

            // Get the min and max scale from the UI.
            int minScale = (int)MinScaleEntry.Value;
            int maxScale = (int)MaxScaleEntry.Value;

            // Re-add selected scales.
            for (int i = minScale; i < maxScale; i++)
            {
                basemapParams.LevelIds.Add(i);
            }

            // Expand the area of interest based on the specified buffer distance.
            int bufferDistance = (int)ExtentBufferEntry.Value;
            basemapParams.AreaOfInterest = GeometryEngine.BufferGeodetic(_areaOfInterest, bufferDistance, LinearUnits.Meters);
        }

        private void ConfigureLayerExclusion(GenerateOfflineMapParameterOverrides overrides)
        {
            // Apply layer exclusions as specified in the UI.
            if (ServiceConnCheckbox.IsChecked == false)
            {
                ExcludeLayerByName("Service Connection", overrides);
            }

            if (SysValvesLayerCheckbox.IsChecked == false)
            {
                ExcludeLayerByName("System Valve", overrides);
            }
        }

        private void CropWaterPipes(GenerateOfflineMapParameterOverrides overrides)
        {
            if (CropLayerCheckbox.IsChecked == true)
            {
                // Get the ID of the water pipes layer.
                long targetLayerId = GetServiceLayerId(GetLayerByName("Main"));

                // For each layer option.
                foreach (GenerateLayerOption layerOption in GetAllLayerOptions(overrides))
                {
                    // If the option's LayerId matches the selected layer's ID.
                    if (layerOption.LayerId == targetLayerId)
                    {
                        layerOption.UseGeometry = true;
                    }
                }
            }
        }

        private void ApplyFeatureFilter(GenerateOfflineMapParameterOverrides overrides)
        {
            // For each layer option.
            foreach (GenerateLayerOption option in GetAllLayerOptions(overrides))
            {
                // If the option's LayerId matches the selected layer's ID.
                if (option.LayerId == GetServiceLayerId(GetLayerByName("Hydrant")))
                {
                    // Apply the where clause.
                    option.WhereClause = "FLOW >= " + (int)FlowRateFilterEntry.Value;

                    // Configure the option to use the where clause.
                    option.QueryOption = GenerateLayerQueryOption.UseFilter;
                }
            }
        }

        private IList<GenerateLayerOption> GetAllLayerOptions(GenerateOfflineMapParameterOverrides overrides)
        {
            // Find the first feature layer.
            FeatureLayer targetLayer = MyMapView.Map.OperationalLayers.OfType<FeatureLayer>().First();

            // Get the key for the layer.
            OfflineMapParametersKey layerKey = new OfflineMapParametersKey(targetLayer);

            // Use that key to get the generate options for the layer.
            GenerateGeodatabaseParameters generateParams = overrides.GenerateGeodatabaseParameters[layerKey];

            // Return the layer options.
            return generateParams.LayerOptions;
        }

        private void ExcludeLayerByName(string layerName, GenerateOfflineMapParameterOverrides overrides)
        {
            // Get the feature layer with the specified name.
            FeatureLayer targetLayer = GetLayerByName(layerName);

            // Get the layer's ID.
            long targetLayerId = GetServiceLayerId(targetLayer);

            // Create a layer key for the selected layer.
            OfflineMapParametersKey layerKey = new OfflineMapParametersKey(targetLayer);

            // Get the parameters for the layer.
            GenerateGeodatabaseParameters generateParams = overrides.GenerateGeodatabaseParameters[layerKey];

            // Get the layer options for the layer.
            IList<GenerateLayerOption> layerOptions = generateParams.LayerOptions;

            // Find the layer option matching the ID.
            GenerateLayerOption targetLayerOption = layerOptions.First(layer => layer.LayerId == targetLayerId);

            // Remove the layer option.
            layerOptions.Remove(targetLayerOption);
        }

        private FeatureLayer GetLayerByName(string layerName)
        {
            // Get the first map in the operational layers collection that is a feature layer with name matching layerName
            return MyMapView.Map.OperationalLayers.OfType<FeatureLayer>().First(layer => layer.Name == layerName);
        }

        private long GetServiceLayerId(FeatureLayer layer)
        {
            // Find the service feature table for the layer; this assumes the layer is backed by a service feature table.
            ServiceFeatureTable serviceTable = (ServiceFeatureTable) layer.FeatureTable;

            // Return the layer ID.
            return serviceTable.LayerInfo.ServiceLayerId;
        }

        #endregion overrides

        // Show changes in job progress.
        private async void OfflineMapJob_ProgressChanged(object sender, EventArgs e)
        {
            // Get the job.
            GenerateOfflineMapJob job = sender as GenerateOfflineMapJob;

            // Dispatch to the UI thread.
            await Dispatcher.RunAsync( Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
            {
                // Show the percent complete and update the progress bar.
                Percentage.Text = job.Progress > 0 ? job.Progress.ToString() + " %" : string.Empty;
                ProgressBar.Value = job.Progress;
            });
        }

        private void CancelJobButton_Click(object sender, Windows.UI.Xaml.RoutedEventArgs e)
        {
            // The user canceled the job.
            _generateOfflineMapJob.Cancel();
        }

        #region Authentication
        // Constants for OAuth-related values.
        // - The URL of the portal to authenticate with (ArcGIS Online).
        private const string ServerUrl = "https://www.arcgis.com/sharing/rest";
        // - The Client ID for an app registered with the server (the ID below is for a public app created by the ArcGIS Runtime team).
        private const string AppClientId = @"lgAdHkYZYlwwfAhC";
        // - An optional client secret for the app (only needed for the OAuthAuthorizationCode authorization type).
        private const string ClientSecret = "";
        // - A URL for redirecting after a successful authorization (this must be a URL configured with the app).
        private const string OAuthRedirectUrl = @"my-ags-app://auth";

        private void SetOAuthInfo()
        {
            // Register the server information with the AuthenticationManager.
            ServerInfo serverInfo = new ServerInfo
            {
                ServerUri = new Uri(ServerUrl),
                TokenAuthenticationType = TokenAuthenticationType.OAuthImplicit,
                OAuthClientInfo = new OAuthClientInfo
                {
                    ClientId = AppClientId,
                    RedirectUri = new Uri(OAuthRedirectUrl)
                }
            };

            // Register this server with AuthenticationManager.
            AuthenticationManager.Current.RegisterServer(serverInfo);

            // Use a function in this class to challenge for credentials.
            AuthenticationManager.Current.ChallengeHandler = new ChallengeHandler(CreateCredentialAsync);

            // Note: In a WPF app, you need to associate a custom IOAuthAuthorizeHandler component with the AuthenticationManager to 
            //     handle showing OAuth login controls (AuthenticationManager.Current.OAuthAuthorizeHandler = new MyOAuthAuthorize();).
            //     The UWP AuthenticationManager, however, uses a built-in IOAuthAuthorizeHandler (based on WebAuthenticationBroker).
        }

        public async Task<Credential> CreateCredentialAsync(CredentialRequestInfo info)
        {
            // ChallengeHandler function for AuthenticationManager that will be called whenever a secured resource is accessed.
            Credential credential = null;

            try
            {
                // AuthenticationManager will handle challenging the user for credentials.
                credential = await AuthenticationManager.Current.GenerateCredentialAsync(info.ServiceUri);
            }
            catch (Exception)
            {
                // Exception will be reported in calling function.
                throw;
            }

            return credential;
        }
        #endregion
    }
}
XAML
<UserControl
    x:Class="ArcGISRuntime.UWP.Samples.GenerateOfflineMapWithOverrides.GenerateOfflineMapWithOverrides"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:esriUI="using:Esri.ArcGISRuntime.UI.Controls">
    <Grid>
        <esriUI:MapView x:Name="MyMapView" />

        <Border x:Name="TakeOfflineArea" Style="{StaticResource BorderStyle}">
            <Grid ColumnSpacing="5" RowSpacing="5">
                <Grid.Resources>
                    <Style TargetType="TextBlock">
                        <Setter Property="Margin" Value="0,0,0,5" />
                    </Style>
                    <Style TargetType="TextBox">
                        <Setter Property="Margin" Value="0,0,0,5" />
                        <Setter Property="VerticalContentAlignment" Value="Center"></Setter>
                    </Style>
                    <Style TargetType="CheckBox">
                        <Setter Property="Margin" Value="0,0,0,5" />
                    </Style>
                    <Style TargetType="Slider">
                        <Setter Property="Margin" Value="0,-8,0,0" />
                        <Setter Property="VerticalAlignment" Value="Center" />
                    </Style>
                </Grid.Resources>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto" />
                    <ColumnDefinition Width="*" />
                    <ColumnDefinition Width="Auto" />
                </Grid.ColumnDefinitions>
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="Auto" />
                </Grid.RowDefinitions>
                <TextBlock Text="Adjust the basemap"
                           FontWeight="SemiBold"
                           Grid.Row="0" Grid.Column="0" />
                <TextBlock Text="Min scale:"
                           Grid.Row="1" Grid.Column="0" />
                <Slider x:Name="MinScaleEntry"
                         Value="0" SnapsTo="StepValues" StepFrequency="1"
                         Minimum="0" Maximum="23"
                         Grid.Row="1" Grid.Column="1" />
                <TextBlock Text="{Binding ElementName=MinScaleEntry,Path=Value}"
                           Grid.Row="1" Grid.Column="2" />
                <TextBlock Text="Max scale:"
                           Grid.Row="2" Grid.Column="0" />
                <Slider x:Name="MaxScaleEntry"
                         Value="23" SnapsTo="StepValues" StepFrequency="1"
                         Minimum="0" Maximum="23"
                         Grid.Row="2" Grid.Column="1" />
                <TextBlock Text="{Binding ElementName=MaxScaleEntry,Path=Value}"
                           Grid.Row="2" Grid.Column="2" />
                <TextBlock Text="Extent buffer distance (m):"
                           Grid.Row="3" Grid.Column="0" />
                <Slider x:Name="ExtentBufferEntry"
                         Value="250" SnapsTo="StepValues" StepFrequency="1"
                         Minimum="0" Maximum="500"
                         Grid.Row="3" Grid.Column="1" />
                <TextBlock Text="{Binding ElementName=ExtentBufferEntry,Path=Value}"
                           Grid.Row="3" Grid.Column="2" />
                <TextBlock Text="Choose layers"
                           FontWeight="SemiBold"
                           Grid.Row="4" Grid.Column="0" />
                <CheckBox x:Name="SysValvesLayerCheckbox"
                          Content="System Valves"
                          IsChecked="True"
                          Grid.Row="5" Grid.Column="0" />
                <CheckBox x:Name="ServiceConnCheckbox"
                          Content="Service Connections"
                          Grid.Row="6" Grid.Column="0" />
                <TextBlock Text="Apply a feature layer filer"
                           FontWeight="SemiBold"
                           Grid.Row="7" Grid.Column="0" />
                <TextBlock Text="Min Hydrant Flow Rate (GPM):"
                           VerticalAlignment="Center"
                           Grid.Row="8" Grid.Column="0" />
                <Slider x:Name="FlowRateFilterEntry"
                         Value="500" SnapsTo="StepValues" StepFrequency="1"
                         Minimum="0" Maximum="999"
                         Margin="0,8,0,0"
                         Grid.Row="8" Grid.Column="1" />
                <TextBlock Text="{Binding ElementName=FlowRateFilterEntry,Path=Value}"
                           Grid.Row="8" Grid.Column="2"
                           VerticalAlignment="Center"
                           MinWidth="25" />
                <TextBlock Text="Crop layer to extent"
                           FontWeight="SemiBold"
                           Grid.Row="9" Grid.Column="0" />
                <CheckBox x:Name="CropLayerCheckbox"
                          Content="Water pipes"
                          IsChecked="True"
                          Grid.Row="10" Grid.Column="0" />
                <Button Content="Take map offline"
                        Grid.Row="11" Grid.Column="0" Grid.ColumnSpan="3"
                        HorizontalAlignment="Stretch"
                        IsEnabled="True" 
                        Click="TakeMapOfflineButton_Click" />
            </Grid>
        </Border>

        <Border x:Name="MessageArea" Visibility="Collapsed"
                Background="White" BorderBrush="Black" BorderThickness="1"
                HorizontalAlignment="Right" VerticalAlignment="Top"
                Margin="30" Padding="5" Width="450">
            <StackPanel>
                <TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Margin="10"
                           FontWeight="Bold"
                           Text="Map is offline!" />
            </StackPanel>
        </Border>

        <Grid x:Name="BusyIndicator" Background="#807f7f7f" Visibility="Collapsed">
            <Grid HorizontalAlignment="Center" VerticalAlignment="Center">
                <Grid.RowDefinitions>
                    <RowDefinition Height="auto" />
                    <RowDefinition Height="auto" />
                    <RowDefinition Height="auto" />
                </Grid.RowDefinitions>
                <TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Margin="10"
                           Foreground="White" FontSize="18">
                    <Run Text="Generating offline map... " />
                    <Run x:Name="Percentage" Text="" />
                </TextBlock>
                <ProgressBar x:Name="ProgressBar"
                             Grid.Row="1"
                             Minimum="0" Maximum="100"
                             IsEnabled="True"
                             HorizontalAlignment="Center" VerticalAlignment="Center"
                             Width="100" Height="10" Margin="0,0,0,10" />
                <Button Grid.Row="2"
                        Content="Cancel"
                        Click="CancelJobButton_Click"
                        HorizontalAlignment="Center"
                        Width="100" />
            </Grid>
        </Grid>
        <Grid x:Name="LoadingIndicator"
              Background="#807f7f7f"
              Visibility="Visible">
            <Grid HorizontalAlignment="Center" VerticalAlignment="Center">
                <Grid.RowDefinitions>
                    <RowDefinition Height="auto" />
                    <RowDefinition Height="auto" />
                </Grid.RowDefinitions>
                <TextBlock Text="Loading online map..."
                           Foreground="White" FontSize="18"
                           Margin="10" />
                <ProgressBar Grid.Row="1"
                             IsEnabled="True" IsIndeterminate="True"
                             Width="100" Height="10"
                             HorizontalAlignment="Center" VerticalAlignment="Center" />
            </Grid>
        </Grid>
    </Grid>
</UserControl>
Examples

Xamarin Forms UWP

Example Name: GenerateOfflineMapWithOverrides

Take a web map offline with additional options for each layer.

Code example screen shot.

C#
using System;
using System.Collections.Generic;
using System.Linq;
using Esri.ArcGISRuntime.Data;
using Esri.ArcGISRuntime.Geometry;
using Esri.ArcGISRuntime.Mapping;
using Esri.ArcGISRuntime.Tasks.Offline;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace ArcGISRuntimeXamarin.Samples.GenerateOfflineMapWithOverrides
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class ConfigureOverridesPage : ContentPage
    {
        private Esri.ArcGISRuntime.Mapping.Map _map;
        private Envelope _areaOfInterest = new Envelope(-88.1541, 41.7690, -88.1471, 41.7720, SpatialReferences.Wgs84);
        private GenerateOfflineMapParameterOverrides _overrides;

        public ConfigureOverridesPage ()
        {
            InitializeComponent ();
        }

        public ConfigureOverridesPage(GenerateOfflineMapParameterOverrides overrides, Esri.ArcGISRuntime.Mapping.Map map)
        {
            InitializeComponent();

            // Hold a reference to the map & overrides.
            _map = map;
            _overrides = overrides;
        }

        #region overrides

        private void ConfigureTileLayerOverrides(GenerateOfflineMapParameterOverrides overrides)
        {
            // Create a parameter key for the first basemap layer.
            OfflineMapParametersKey basemapKey = new OfflineMapParametersKey(_map.Basemap.BaseLayers.First());

            // Get the export tile cache parameters for the layer key.
            ExportTileCacheParameters basemapParams = overrides.ExportTileCacheParameters[basemapKey];

            // Clear the existing level IDs.
            basemapParams.LevelIds.Clear();

            // Get the min and max scale from the UI.
            int minScale = (int)MinScaleEntry.Value;
            int maxScale = (int) MaxScaleEntry.Value;

            // Re-add selected scales.
            for (int i = minScale; i < maxScale; i++)
            {
                basemapParams.LevelIds.Add(i);
            }

            // Expand the area of interest based on the specified buffer distance.
            int bufferDistance = (int)ExtentBufferEntry.Value;
            basemapParams.AreaOfInterest = GeometryEngine.BufferGeodetic(_areaOfInterest, bufferDistance, LinearUnits.Meters);
        }

        private void ConfigureLayerExclusion(GenerateOfflineMapParameterOverrides overrides)
        {
            // Apply layer exclusions as specified in the UI.
            if (ServiceConnCheckbox.IsToggled == false)
            {
                ExcludeLayerByName("Service Connection", overrides);
            }

            if (SysValvesLayerCheckbox.IsToggled == false)
            {
                ExcludeLayerByName("System Valve", overrides);
            }
        }

        private void CropWaterPipes(GenerateOfflineMapParameterOverrides overrides)
        {
            if (CropLayerCheckbox.IsToggled)
            {
                // Get the ID of the water pipes layer.
                long targetLayerId = GetServiceLayerId(GetLayerByName("Main"));

                // For each layer option.
                foreach (GenerateLayerOption layerOption in GetAllLayerOptions(overrides))
                {
                    // If the option's LayerId matches the selected layer's ID.
                    if (layerOption.LayerId == targetLayerId)
                    {
                        layerOption.UseGeometry = true;
                    }
                }
            }
        }

        private void ApplyFeatureFilter(GenerateOfflineMapParameterOverrides overrides)
        {
            // For each layer option.
            foreach (GenerateLayerOption option in GetAllLayerOptions(overrides))
            {
                // If the option's LayerId matches the selected layer's ID.
                if (option.LayerId == GetServiceLayerId(GetLayerByName("Hydrant")))
                {
                    // Apply the where clause.
                    option.WhereClause = "FLOW >= " + (int)FlowRateFilterEntry.Value;
                    // Configure the option to use the where clause.
                    option.QueryOption = GenerateLayerQueryOption.UseFilter;
                }
            }
        }

        private IList<GenerateLayerOption> GetAllLayerOptions(GenerateOfflineMapParameterOverrides overrides)
        {
            // Find the first feature layer.
            FeatureLayer targetLayer = _map.OperationalLayers.OfType<FeatureLayer>().First();

            // Get the key for the layer.
            OfflineMapParametersKey layerKey = new OfflineMapParametersKey(targetLayer);

            // Use that key to get the generate options for the layer.
            GenerateGeodatabaseParameters generateParams = overrides.GenerateGeodatabaseParameters[layerKey];

            // Return the layer options.
            return generateParams.LayerOptions;
        }

        private void ExcludeLayerByName(string layerName, GenerateOfflineMapParameterOverrides overrides)
        {
            // Get the feature layer with the specified name.
            FeatureLayer targetLayer = GetLayerByName(layerName);

            // Get the layer's ID.
            long targetLayerId = GetServiceLayerId(targetLayer);

            // Create a layer key for the selected layer.
            OfflineMapParametersKey layerKey = new OfflineMapParametersKey(targetLayer);

            // Get the parameters for the layer.
            GenerateGeodatabaseParameters generateParams = overrides.GenerateGeodatabaseParameters[layerKey];

            // Get the layer options for the layer.
            IList<GenerateLayerOption> layerOptions = generateParams.LayerOptions;

            // Find the layer option matching the ID.
            GenerateLayerOption targetLayerOption = layerOptions.First(layer => layer.LayerId == targetLayerId);

            // Remove the layer option.
            layerOptions.Remove(targetLayerOption);
        }

        private FeatureLayer GetLayerByName(string layerName)
        {
            // Get the first map in the operational layers collection that is a feature layer with name matching layerName
            return _map.OperationalLayers.OfType<FeatureLayer>().First(layer => layer.Name == layerName);
        }

        private long GetServiceLayerId(FeatureLayer layer)
        {
            // Find the service feature table for the layer; this assumes the layer is backed by a service feature table.
            ServiceFeatureTable serviceTable = (ServiceFeatureTable) layer.FeatureTable;

            // Return the layer ID.
            return serviceTable.LayerInfo.ServiceLayerId;
        }

        #endregion overrides

        private void TakeMapOffline_Clicked(object sender, EventArgs e)
        {
            // Configure the overrides using helper methods.
            ConfigureTileLayerOverrides(_overrides);
            ConfigureLayerExclusion(_overrides);
            CropWaterPipes(_overrides);
            ApplyFeatureFilter(_overrides);

            // The main sample page continues when OnDisappearing is called.
            // OnDisappearing is called by Xamarin.Forms when navigation away from this page happens.
            Navigation.PopModalAsync(true);
        }
    }
}
C#
// Copyright 2019 Esri.
// 
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
// You may obtain a copy of the License at: http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an 
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific 
// language governing permissions and limitations under the License.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Esri.ArcGISRuntime.Geometry;
using Esri.ArcGISRuntime.Mapping;
using Esri.ArcGISRuntime.Symbology;
using Esri.ArcGISRuntime.Tasks;
using Esri.ArcGISRuntime.Tasks.Offline;
using Esri.ArcGISRuntime.UI;
using Esri.ArcGISRuntime.Portal;
using Esri.ArcGISRuntime.Security;
using Xamarin.Forms;
#if __IOS__
using Xamarin.Auth;
using UIKit;
#endif

#if __ANDROID__
using Android.App;
using Application = Xamarin.Forms.Application;
using Xamarin.Auth;
#endif

namespace ArcGISRuntimeXamarin.Samples.GenerateOfflineMapWithOverrides
{
    [ArcGISRuntime.Samples.Shared.Attributes.Sample(
        name: "Generate offline map (overrides)",
        category: "Map",
        description: "Take a web map offline with additional options for each layer.",
        instructions: "Modify the overrides parameters:",
        tags: new[] { "LOD", "adjust", "download", "extent", "filter", "offline", "override", "parameters", "reduce", "scale range", "setting" })]
    public partial class GenerateOfflineMapWithOverrides : ContentPage, IOAuthAuthorizeHandler
    {
        // The job to generate an offline map.
        private GenerateOfflineMapJob _generateOfflineMapJob;

        // The extent of the data to take offline.
        private Envelope _areaOfInterest = new Envelope(-88.1541, 41.7690, -88.1471, 41.7720, SpatialReferences.Wgs84);

        // The ID for a web map item hosted on the server (water network map of Naperville IL).
        private const string WebMapId = "acc027394bc84c2fb04d1ed317aac674";

        public GenerateOfflineMapWithOverrides()
        {
            InitializeComponent();

            // Load the web map, show area of interest, restrict map interaction, and set up authorization. 
            Initialize();
        }

        private async void Initialize()
        {
            try
            {
                // Call a function to set up the AuthenticationManager for OAuth.
                SetOAuthInfo();

                // Create the ArcGIS Online portal.
                ArcGISPortal portal = await ArcGISPortal.CreateAsync();

                // Get the Naperville water web map item using its ID.
                PortalItem webmapItem = await PortalItem.CreateAsync(portal, WebMapId);

                // Create a map from the web map item.
                Map onlineMap = new Map(webmapItem);

                // Display the map in the MapView.
                MyMapView.Map = onlineMap;

                // Disable user interactions on the map (no panning or zooming from the initial extent).
                MyMapView.InteractionOptions = new MapViewInteractionOptions
                {
                    IsEnabled = false
                };

                // Create a graphics overlay for the extent graphic and apply a renderer.
                SimpleLineSymbol aoiOutlineSymbol = new SimpleLineSymbol(SimpleLineSymbolStyle.Solid, System.Drawing.Color.Red, 3);
                GraphicsOverlay extentOverlay = new GraphicsOverlay
                {
                    Renderer = new SimpleRenderer(aoiOutlineSymbol)
                };
                MyMapView.GraphicsOverlays.Add(extentOverlay);

                // Add a graphic to show the area of interest (extent) that will be taken offline.
                Graphic aoiGraphic = new Graphic(_areaOfInterest);
                extentOverlay.Graphics.Add(aoiGraphic);

                // Hide the map loading progress indicator.
                loadingIndicator.IsVisible = false;
            }
            catch (Exception ex)
            {
                await Application.Current.MainPage.DisplayAlert("Alert", ex.ToString(), "OK");
            }
        }

        private async void TakeMapOfflineButton_Click(object sender, EventArgs e)
        {
            // Clean up any previous outputs in the temp directory.
            string tempPath = $"{Path.GetTempPath()}";
            string[] outputFolders = Directory.GetDirectories(tempPath, "NapervilleWaterNetwork*");

            // Loop through the folder names and delete them.
            foreach (string dir in outputFolders)
            {
                try
                {
                    // Delete the folder.
                    Directory.Delete(dir, true);
                }
                catch (Exception)
                {
                    // Ignore exceptions (files might be locked, for example).
                }
            }

            // Create a new folder for the output mobile map.
            string packagePath = Path.Combine(tempPath, @"NapervilleWaterNetwork");
            int num = 1;
            while (Directory.Exists(packagePath))
            {
                packagePath = Path.Combine(tempPath, @"NapervilleWaterNetwork" + num.ToString());
                num++;
            }

            // Create the output directory.
            Directory.CreateDirectory(packagePath);

            try
            {
                // Show the progress indicator while the job is running.
                busyIndicator.IsVisible = true;

                // Create an offline map task with the current (online) map.
                OfflineMapTask takeMapOfflineTask = await OfflineMapTask.CreateAsync(MyMapView.Map);

                // Create the default parameters for the task, pass in the area of interest.
                GenerateOfflineMapParameters parameters = await takeMapOfflineTask.CreateDefaultGenerateOfflineMapParametersAsync(_areaOfInterest);

                // Generate parameter overrides for more in-depth control of the job.
                GenerateOfflineMapParameterOverrides overrides = await takeMapOfflineTask.CreateGenerateOfflineMapParameterOverridesAsync(parameters);

                // Create the UI for configuring the overrides.
                ConfigureOverridesPage configurationPage = new ConfigureOverridesPage(overrides, MyMapView.Map);

                // Wait for the user to finish with the configuration, then continue.
                configurationPage.Disappearing += async (o, args) =>
                {
                    // Re-show the busy indicator.
                    busyIndicator.IsVisible = true;

                    // Create the job with the parameters and output location.
                    _generateOfflineMapJob = takeMapOfflineTask.GenerateOfflineMap(parameters, packagePath, overrides);

                    // Handle the progress changed event for the job.
                    _generateOfflineMapJob.ProgressChanged += OfflineMapJob_ProgressChanged;

                    // Await the job to generate geodatabases, export tile packages, and create the mobile map package.
                    GenerateOfflineMapResult results = await _generateOfflineMapJob.GetResultAsync();

                    // Check for job failure (writing the output was denied, e.g.).
                    if (_generateOfflineMapJob.Status != JobStatus.Succeeded)
                    {
                        await Application.Current.MainPage.DisplayAlert("Alert", "Generate offline map package failed.", "OK");
                        busyIndicator.IsVisible = false;
                    }

                    // Check for errors with individual layers.
                    if (results.LayerErrors.Any())
                    {
                        // Build a string to show all layer errors.
                        System.Text.StringBuilder errorBuilder = new System.Text.StringBuilder();
                        foreach (KeyValuePair<Layer, Exception> layerError in results.LayerErrors)
                        {
                            errorBuilder.AppendLine($"{layerError.Key.Id} : {layerError.Value.Message}");
                        }

                        // Show layer errors.
                        string errorText = errorBuilder.ToString();
                        await Application.Current.MainPage.DisplayAlert("Alert", errorText, "OK");
                    }

                    // Display the offline map.
                    MyMapView.Map = results.OfflineMap;

                    // Apply the original viewpoint for the offline map.
                    MyMapView.SetViewpoint(new Viewpoint(_areaOfInterest));

                    // Enable map interaction so the user can explore the offline data.
                    MyMapView.InteractionOptions.IsEnabled = true;

                    // Hide the "Take map offline" button.
                    takeOfflineArea.IsVisible = false;

                    // Show a message that the map is offline.
                    messageArea.IsVisible = true;

                    // Hide the busy indicator.
                    busyIndicator.IsVisible = false;
                };

                // Show the configuration UI.
                await Navigation.PushModalAsync(configurationPage, true);

            }
            catch (TaskCanceledException)
            {
                // Generate offline map task was canceled.
                await Application.Current.MainPage.DisplayAlert("Alert", "Taking map offline was canceled", "OK");
            }
            catch (Exception ex)
            {
                // Exception while taking the map offline.
                await Application.Current.MainPage.DisplayAlert("Alert", ex.Message, "OK");
            }
            finally
            {
                // Hide the activity indicator when the job is done.
                busyIndicator.IsVisible = false;
            }
        }

        // Show changes in job progress.
        private void OfflineMapJob_ProgressChanged(object sender, EventArgs e)
        {
            // Get the job.
            GenerateOfflineMapJob job = sender as GenerateOfflineMapJob;

            // Dispatch to the UI thread.
            Device.BeginInvokeOnMainThread(() =>
            {
                // Show the percent complete and update the progress bar.
                Percentage.Text = job.Progress > 0 ? job.Progress.ToString() + " %" : string.Empty;
                progressBar.Progress = job.Progress / 100.0;
            });
        }

        private void CancelJobButton_Click(object sender, EventArgs e)
        {
            // The user canceled the job.
            _generateOfflineMapJob.Cancel();
        }

        #region Authentication

        // Constants for OAuth-related values.
        // - The URL of the portal to authenticate with (ArcGIS Online).
        private const string ServerUrl = "https://www.arcgis.com/sharing/rest";

        // - The Client ID for an app registered with the server (the ID below is for a public app created by the ArcGIS Runtime team).
        private const string AppClientId = @"6wMAmbUEX1rvsOb4";

        // - A URL for redirecting after a successful authorization (this must be a URL configured with the app).
        private const string OAuthRedirectUrl = @"forms-samples-app://auth";

        private void SetOAuthInfo()
        {
            // Register the server information with the AuthenticationManager.
            ServerInfo serverInfo = new ServerInfo
            {
                ServerUri = new Uri(ServerUrl),
                TokenAuthenticationType = TokenAuthenticationType.OAuthImplicit,
                OAuthClientInfo = new OAuthClientInfo
                {
                    ClientId = AppClientId,
                    RedirectUri = new Uri(OAuthRedirectUrl)
                }
            };

            // Register this server with AuthenticationManager.
            AuthenticationManager.Current.RegisterServer(serverInfo);

            // Use a function in this class to challenge for credentials.
            AuthenticationManager.Current.ChallengeHandler = new ChallengeHandler(CreateCredentialAsync);

            // Set the OAuthAuthorizeHandler component (this class) for Android or iOS platforms.
#if __ANDROID__ || __IOS__
            AuthenticationManager.Current.OAuthAuthorizeHandler = this;
#endif
        }

        // ChallengeHandler function that will be called whenever access to a secured resource is attempted.
        public async Task<Credential> CreateCredentialAsync(CredentialRequestInfo info)
        {
            Credential credential = null;

            try
            {
                // IOAuthAuthorizeHandler will challenge the user for OAuth credentials.
                credential = await AuthenticationManager.Current.GenerateCredentialAsync(info.ServiceUri);
            }
            catch (TaskCanceledException)
            {
                return credential;
            }
            catch (Exception)
            {
                // Exception will be reported in calling function.
                throw;
            }

            return credential;
        }

        #region IOAuthAuthorizationHandler implementation

        // Use a TaskCompletionSource to track the completion of the authorization.
        private TaskCompletionSource<IDictionary<string, string>> _taskCompletionSource;

        // IOAuthAuthorizeHandler.AuthorizeAsync implementation.
        public Task<IDictionary<string, string>> AuthorizeAsync(Uri serviceUri, Uri authorizeUri, Uri callbackUri)
        {
            // If the TaskCompletionSource is not null, authorization may already be in progress and should be canceled.
            if (_taskCompletionSource != null)
            {
                // Try to cancel any existing authentication task.
                _taskCompletionSource.TrySetCanceled();
            }

            // Create a task completion source.
            _taskCompletionSource = new TaskCompletionSource<IDictionary<string, string>>();
#if __ANDROID__ || __IOS__

#if __ANDROID__
            // Get the current Android Activity.
            Activity activity = (Activity)ArcGISRuntime.Droid.MainActivity.Instance;
#endif
#if __IOS__
            // Get the current iOS ViewController.
            UIViewController viewController = null;
            Device.BeginInvokeOnMainThread(() => { viewController = UIApplication.SharedApplication.KeyWindow.RootViewController; });
#endif
            // Create a new Xamarin.Auth.OAuth2Authenticator using the information passed in.
            OAuth2Authenticator authenticator = new OAuth2Authenticator(
                clientId: AppClientId,
                scope: "",
                authorizeUrl: authorizeUri,
                redirectUrl: callbackUri)
            {
                ShowErrors = false,
                // Allow the user to cancel the OAuth attempt.
                AllowCancel = true
            };

            // Define a handler for the OAuth2Authenticator.Completed event.
            authenticator.Completed += (sender, authArgs) =>
            {
                try
                {
#if __IOS__
                    // Dismiss the OAuth UI when complete.
                    viewController.DismissViewController(true, null);
#endif

                    // Check if the user is authenticated.
                    if (authArgs.IsAuthenticated)
                    {
                        // If authorization was successful, get the user's account.
                        Xamarin.Auth.Account authenticatedAccount = authArgs.Account;

                        // Set the result (Credential) for the TaskCompletionSource.
                        _taskCompletionSource.SetResult(authenticatedAccount.Properties);
                    }
                    else
                    {
                        throw new Exception("Unable to authenticate user.");
                    }
                }
                catch (Exception ex)
                {
                    // If authentication failed, set the exception on the TaskCompletionSource.
                    _taskCompletionSource.TrySetException(ex);

                    // Cancel authentication.
                    authenticator.OnCancelled();
                }
                finally
                {
                    // Dismiss the OAuth login.
#if __ANDROID__
                    activity.FinishActivity(99);
#endif
                }
            };

            // If an error was encountered when authenticating, set the exception on the TaskCompletionSource.
            authenticator.Error += (sndr, errArgs) =>
            {
                // If the user cancels, the Error event is raised but there is no exception ... best to check first.
                if (errArgs.Exception != null)
                {
                    _taskCompletionSource.TrySetException(errArgs.Exception);
                }
                else
                {
                    // Login canceled: dismiss the OAuth login.
                    if (_taskCompletionSource != null)
                    {
                        _taskCompletionSource.TrySetCanceled();
#if __ANDROID__
                        activity.FinishActivity(99);
#endif
                    }
                }

                // Cancel authentication.
                authenticator.OnCancelled();
            };

            // Present the OAuth UI so the user can enter user name and password.
#if __ANDROID__
            Android.Content.Intent intent = authenticator.GetUI(activity);
            activity.StartActivityForResult(intent, 99);
#endif
#if __IOS__
            // Present the OAuth UI (on the app's UI thread) so the user can enter user name and password.
            Device.BeginInvokeOnMainThread(() => { viewController.PresentViewController(authenticator.GetUI(), true, null); });
#endif

#endif // (If Android or iOS)
            // Return completion source task so the caller can await completion.
            return _taskCompletionSource.Task;
        }

        #endregion IOAuthAuthorizationHandler implementation

        #endregion Authentication
    }
}
XAML
<?xml version="1.0" encoding="utf-8" ?>

<ContentPage x:Class="ArcGISRuntimeXamarin.Samples.GenerateOfflineMapWithOverrides.ConfigureOverridesPage" xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" Title="Configure offline map job">
    <Grid Margin="10" VerticalOptions="Center">
        <Grid.Resources>
            <Style TargetType="Label">
                <Setter Property="Margin" Value="0,0,0,5" />
                <Setter Property="VerticalOptions" Value="Center" />
            </Style>
            <Style TargetType="Entry">
                <Setter Property="Margin" Value="0,0,0,5" />
            </Style>
            <Style TargetType="Switch">
                <Setter Property="Margin" Value="0,0,0,5" />
            </Style>
        </Grid.Resources>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="Auto" />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <Label Grid.Row="0" Grid.Column="0" FontAttributes="Bold" Text="Adjust the basemap" />
        <Label Grid.Row="1" Grid.Column="0" Text="Min scale:" />
        <Slider
            x:Name="MinScaleEntry"
            Grid.Row="1"
            Grid.Column="1"
            Maximum="23"
            MaximumTrackColor="CadetBlue"
            Minimum="0"
            MinimumTrackColor="CadetBlue"
            Value="0" />
        <Label Grid.Row="1" Grid.Column="2" Text="{Binding Source={x:Reference MinScaleEntry}, Path=Value, StringFormat='{0:N0}'}" />
        <Label Grid.Row="2" Grid.Column="0" Text="Max scale:" />
        <Slider
            x:Name="MaxScaleEntry"
            Grid.Row="2"
            Grid.Column="1"
            Maximum="23"
            MaximumTrackColor="CadetBlue"
            Minimum="0"
            MinimumTrackColor="CadetBlue"
            Value="23" />
        <Label Grid.Row="2" Grid.Column="2" Text="{Binding Source={x:Reference MaxScaleEntry}, Path=Value, StringFormat='{0:N0}'}" />
        <Label Grid.Row="3" Grid.Column="0" Text="Buffer distance:" />
        <Slider
            x:Name="ExtentBufferEntry"
            Grid.Row="3"
            Grid.Column="1"
            Maximum="500"
            MaximumTrackColor="CadetBlue"
            Minimum="0"
            MinimumTrackColor="CadetBlue"
            Value="250" />
        <Label Grid.Row="3" Grid.Column="2" Text="{Binding Source={x:Reference ExtentBufferEntry}, Path=Value, StringFormat='{0:N0}m'}" />
        <Label Grid.Row="4" Grid.Column="0" FontAttributes="Bold" Text="Choose layers" />
        <Label Grid.Row="5" Grid.Column="0" Text="System valves:" />
        <Switch x:Name="SysValvesLayerCheckbox" Grid.Row="5" Grid.Column="2" IsToggled="True" />
        <Label Grid.Row="6" Grid.Column="0" Text="Service connections:" />
        <Switch x:Name="ServiceConnCheckbox" Grid.Row="6" Grid.Column="2" />
        <Label
            Grid.Row="7"
            Grid.Column="0"
            Margin="0,0,0,5"
            FontAttributes="Bold"
            Text="Apply a feature layer filer" />
        <Label Grid.Row="8" Grid.Column="0" Margin="0,0,5,5" Text="Min Flow Rate:" />
        <Slider
            x:Name="FlowRateFilterEntry"
            Grid.Row="8"
            Grid.Column="1"
            Maximum="999"
            MaximumTrackColor="CadetBlue"
            Minimum="0"
            MinimumTrackColor="CadetBlue"
            Value="500" />
        <Label Grid.Row="8" Grid.Column="2" MinimumWidthRequest="40" Text="{Binding Source={x:Reference FlowRateFilterEntry}, Path=Value, StringFormat='{0:N0} GPM'}" />
        <Label Grid.Row="9" Grid.Column="0" FontAttributes="Bold" Text="Crop layers to extent" />
        <Label Grid.Row="10" Grid.Column="0" Text="Water pipes:" />
        <Switch x:Name="CropLayerCheckbox" Grid.Row="10" Grid.Column="2" IsToggled="True" />
        <Button
            Grid.Row="11"
            Grid.Column="0"
            Grid.ColumnSpan="3"
            Clicked="TakeMapOffline_Clicked"
            IsEnabled="True"
            Text="Take map offline" />
    </Grid>
</ContentPage>
XAML
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"             
             xmlns:esriUI="clr-namespace:Esri.ArcGISRuntime.Xamarin.Forms;assembly=Esri.ArcGISRuntime.Xamarin.Forms"
             xmlns:mapping="clr-namespace:Esri.ArcGISRuntime.Mapping;assembly=Esri.ArcGISRuntime" 
             x:Class="ArcGISRuntimeXamarin.Samples.GenerateOfflineMapWithOverrides.GenerateOfflineMapWithOverrides">
  <Grid>
        <esriUI:MapView x:Name="MyMapView"/>

        <Frame x:Name="takeOfflineArea" 
               BackgroundColor="White" BorderColor="Black"
               HorizontalOptions="End" VerticalOptions="Start"
               Margin="30" Padding="20" WidthRequest="375">
            <StackLayout HorizontalOptions="Center" VerticalOptions="Center">
                <Button x:Name="TakeMapOfflineButton" 
                        WidthRequest="250"
                        Clicked="TakeMapOfflineButton_Click" 
                        IsEnabled="True" 
                        Text="Take map offline"/>
            </StackLayout>
        </Frame>

        <Frame x:Name="messageArea" 
               IsVisible="False"
               BackgroundColor="White" BorderColor="Black"
               HorizontalOptions="End" VerticalOptions="Start"
               Margin="30" Padding="5" WidthRequest="450">
            <StackLayout>
                <Label HorizontalOptions="Center" VerticalOptions="Center" Margin="10"
                       Text="Map is offline!"/>
            </StackLayout>
        </Frame>

        <Grid x:Name="busyIndicator" BackgroundColor="#807f7f7f" IsVisible="False">
            <Grid HorizontalOptions="Center" VerticalOptions="Center">
                <Grid.RowDefinitions>
                    <RowDefinition Height="auto"/>
                    <RowDefinition Height="auto"/>
                    <RowDefinition Height="auto"/>
                </Grid.RowDefinitions>
                <Label HorizontalOptions="Center" VerticalOptions="Center" Margin="10"
                       TextColor="White" FontSize="18">
                    <Label.FormattedText>
                        <FormattedString>
                            <Span Text="Generating offline map... "/>
                            <Span x:Name="Percentage" Text=""/>
                        </FormattedString>
                    </Label.FormattedText>
                </Label>
                <ProgressBar x:Name="progressBar"
                             Grid.Row="1"
                             IsEnabled="True"
                             HorizontalOptions="Center" VerticalOptions="Center" 
                             WidthRequest="100" HeightRequest="10" Margin="0,0,0,10"/>
                <Button x:Name="CancelJobButton" 
                        Grid.Row="3"
                        Text="Cancel" 
                        Clicked="CancelJobButton_Click" 
                        HorizontalOptions="Center" 
                        WidthRequest="100"/>
            </Grid>
        </Grid>
        <Grid x:Name="loadingIndicator" 
              BackgroundColor="#807f7f7f" 
              IsVisible="True">
            <Grid HorizontalOptions="Center" VerticalOptions="Center">
                <Grid.RowDefinitions>
                    <RowDefinition Height="auto"/>
                    <RowDefinition Height="auto"/>
                </Grid.RowDefinitions>
                <Label Text="Loading online map..." 
                           TextColor="White" FontSize="18"
                           Margin="10"/>
                <ProgressBar Grid.Row="1"
                             IsEnabled="True"
                             WidthRequest="100" HeightRequest="10" 
                             HorizontalOptions="Center" VerticalOptions="Center"/>
            </Grid>
        </Grid>
    </Grid>
</ContentPage>
See Also
Additional Examples
Hyperlink to ExampleDescription
GenerateOfflineMapWithOverridesTake a web map offline with additional options for each layer.