/// <summary>
        /// Determines whether the Close button is in an executable state
        /// </summary>
        /// <remarks>
        /// The executable state is determined by whether QueryRelatedViewModel.PopupInfo.Container
        /// container is an InfoWindow. If it is, the command is executable.</remarks>
        private bool canCloseRelationshipView(object parameter)
        {
            popupWindow = PopupInfo.Container as InfoWindow;
            Grid infoWindowGrid = Utils.FindChildOfType <Grid>(popupWindow, 3);

            return(popupWindow != null && infoWindowGrid.Children.Contains(RelationshipView));
        }
        /// <summary>
        /// Determines whether the popupWindow is null
        /// </summary>
        private bool canCloseNoRecordsView(object parameter)
        {
            // Check whether the grid contained in the pop-up window contains the NoRecordsFoundView
            popupWindow = PopupInfo.Container as InfoWindow;
            Grid infoWindowGrid = Utils.FindChildOfType <Grid>(popupWindow, 3);

            return(popupWindow != null && infoWindowGrid.Children.Contains(NoRecordsFoundView));
        }
        /// <summary>
        /// Closes the dialog displaying the message that no related records were found.
        /// </summary>
        private void closeNoRecordsView(object parameter)
        {
            Grid infoWindowGrid = Utils.FindChildOfType <Grid>(popupWindow, 3);

            if (infoWindowGrid.Children.Contains(NoRecordsFoundView))
            {
                infoWindowGrid.Children.Remove(NoRecordsFoundView);
            }
        }
        /// <summary>
        /// Returns to the original pop-up attribute display.
        /// </summary>
        private void goBack(object parameter)
        {
            popupWindow = PopupInfo.Container as InfoWindow;
            //remove the relationship view
            Grid infoWindowGrid = Utils.FindChildOfType <Grid>(popupWindow, 3);

            if (infoWindowGrid.Children.Contains(RelationshipView))
            {
                infoWindowGrid.Children.Remove(RelationshipView);
            }
        }
        /// <summary>
        /// Closes the pop-up window
        /// </summary>
        private void closeRelationshipView(object parameter)
        {
            // Remove the list of relationships from the pop-up and close it
            popupWindow = PopupInfo.Container as InfoWindow;
            Grid infoWindowGrid = Utils.FindChildOfType <Grid>(popupWindow, 3);

            if (infoWindowGrid.Children.Contains(RelationshipView))
            {
                infoWindowGrid.Children.Remove(RelationshipView);
            }
            popupWindow.IsOpen = false;
        }
        /// <summary>
        /// Handle successful query task and initializes the results layer
        /// </summary>
        private void QueryTask_ExecuteRelationshipQueryCompleted(object sender, RelationshipEventArgs e)
        {
            // Unhook the queryTask.ExecuteRelationshipQueryCompleted
            queryTask.ExecuteRelationshipQueryCompleted -= QueryTask_ExecuteRelationshipQueryCompleted;

            // queryResult used once the results layer is initialized
            queryResult = e.Result;

            if (queryResult.RelatedRecordsGroup.Count > 0)
            {
                string relatedTableID = SelectedRelationship.RelatedTableId.ToString();

                if (relatedTableID == null)
                {
                    relatedTableID = relatesLayer.LayerInfo.Relationships.First().RelatedTableId.ToString();
                }

                // Determine the url for the related table and use to populate the Url property of the new resultsLayer.
                string resultsUrl = relatesLayer.Url;

                resultsUrl = resultsUrl.Substring(0, resultsUrl.LastIndexOf('/') + 1);
                resultsUrl = resultsUrl.Insert(resultsUrl.Length, relatedTableID);


                // Create a FeatureLayer for the results based on the url of the related records. At this point, the feature layer will
                // retrieve all the graphics/info from the layer. In the resultsLayer_Initialized, we trim that down based on the Object id field.
                resultsLayer = new FeatureLayer()
                {
                    Url      = resultsUrl,
                    ProxyUrl = relatesLayer.ProxyUrl
                };

                resultsLayer.OutFields.Add("*");

                // Initialize the resultsLayer to populate layer metadata (LayerInfo) so the OID field can be retrieved.
                resultsLayer.Initialized          += resultsLayer_Initialized;
                resultsLayer.InitializationFailed += resultsLayer_InitializationFailed;
                resultsLayer.UpdateCompleted      += resultsLayer_UpdateCompleted;

                resultsLayer.Initialize();
            }
            else
            {
                Grid infoWindowGrid = Utils.FindChildOfType <Grid>(popupWindow, 3);
                infoWindowGrid.Children.Remove(indicator);

                infoWindowGrid.Children.Add(NoRecordsFoundView);

                ((DelegateCommand)CloseNoRecordsView).RaiseCanExecuteChanged();
            }
        }
예제 #7
0
        ///<summary>
        /// When moving between features in the pop-up window, this removes the "Keep on map" checkbox until a new query is executed.
        ///</summary>
        private void PopupItem_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            popupInfo.PropertyChanged -= PopupItem_PropertyChanged;

            if (e.PropertyName == "SelectedIndex")
            {
                if (popupWindow != null)
                {
                    // Remove the Keep on Map View (checkbox) if appropriate.
                    Grid infoWindowGrid = Utils.FindChildOfType <Grid>(popupWindow, 3);
                    if (infoWindowGrid.Children.Contains(keepOnMapView))
                    {
                        infoWindowGrid.Children.Remove(keepOnMapView);
                    }
                }
            }
        }
        /// <summary>
        /// Handle events when the popup window closes
        /// </summary>
        private void OnIsOpenChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            // Popup window is closed
            if (!popupWindow.IsOpen)
            {
                // Pop-up is closing, so detach PopupInfo.PropertyChanged and MapApplication.Current.SelectedLayerChanged events
                PopupInfo.PropertyChanged -= PopupInfo_PropertyChanged;
                MapApplication.Current.SelectedLayerChanged -= Current_SelectedLayerChanged;

                // If the pop-up is closing and the temporary layer still exists, remove it from the map.
                if (temporaryLayer != null)
                {
                    if (MapApplication.Current.SelectedLayer == temporaryLayer)
                    {
                        HideFeatureDataGrid();
                    }

                    if (map.Layers.Contains(temporaryLayer))
                    {
                        map.Layers.Remove(temporaryLayer);
                    }

                    temporaryLayer = null;
                }


                Grid infoWindowGrid = Utils.FindChildOfType <Grid>(popupWindow, 3);
                if (infoWindowGrid.Children.Contains(indicator))
                {
                    infoWindowGrid.Children.Remove(indicator); // Remove the busy indicator from the pop-up window
                }
                if (CloseNoRecordsView.CanExecute(popupWindow))
                {
                    CloseNoRecordsView.Execute(popupWindow);
                }

                if (GoBack.CanExecute(popupWindow))
                {
                    GoBack.Execute(popupWindow);
                }

                _popupItemChanged = false;
            }
        }
예제 #9
0
        /// <summary>
        /// Retrieves the pop-up info and determines whether there is more than one relationship for the layer. If there is more
        /// than one, the relationships are displayed in a list for the user to choose from before proceeding.
        /// </summary>
        /// <param name="parameter">OnClickPopupInfo from clicked layer</param>
        public void Execute(object parameter)
        {
            relationshipList.Clear();

            OnClickPopupInfo popupInfo = parameter as OnClickPopupInfo;

            popupWindow = popupInfo.Container as InfoWindow;

            // Instantiate the View Model
            if (vm == null)
            {
                vm = new QueryRelatedViewModel(relationshipList, MapApplication.Current.Map);
            }

            // Instantiate the Relationship View
            if (relationshipView == null)
            {
                relationshipView = new RelationshipSelectionView();
            }

            // Instantiate the Keep on Map View
            if (keepOnMapView == null)
            {
                keepOnMapView = new KeepOnMap();
            }

            // Instantiate the No results found View
            if (noRecordsView == null)
            {
                noRecordsView = new NoRecordsFoundView();
            }

            // Set the variables on the ViewModel
            vm.PopupInfo          = popupInfo;
            vm.KeepOnMapView      = keepOnMapView;
            vm.RelationshipView   = relationshipView;
            vm.NoRecordsFoundView = noRecordsView;

            // Set the data context of the RelationshipView and the KeepOnMapView to the QueryRelatedViewModel
            relationshipView.DataContext = vm;
            keepOnMapView.DataContext    = vm;
            noRecordsView.DataContext    = vm;

            // Get the layer info from the pop-up to access the Relationships. Verify the layer is a FeatureLayer to proceed.
            FeatureLayer relatesLayer;
            Layer        lyr = popupInfo.PopupItem.Layer;

            if (lyr is FeatureLayer)
            {
                relatesLayer = lyr as FeatureLayer;

                // Check the number of relationships for the layer
                int relCount = relatesLayer.LayerInfo.Relationships.Count();
                if (relCount > 1) // Layer has more than one relationship
                {
                    foreach (Relationship rel in relatesLayer.LayerInfo.Relationships)
                    {
                        relationshipList.Add(rel);
                    }

                    numberOfRelates = string.Format(Strings.RelationshipsFound,
                                                    relatesLayer.LayerInfo.Relationships.Count().ToString());
                    vm.NumberOfRelationships = numberOfRelates;

                    // Add the available relationships view to the popupWindow.
                    Grid infoWindowGrid = Utils.FindChildOfType <Grid>(popupWindow, 3);
                    infoWindowGrid.Children.Add(relationshipView);


                    // Can now navigate back to attributes and close the popup from the relationship view,
                    // so notify about change in executable state
                    ((DelegateCommand)vm.GoBack).RaiseCanExecuteChanged();
                    ((DelegateCommand)vm.CloseRelationshipView).RaiseCanExecuteChanged();
                }
                else // Layer only has one relationship, so can use Relationship.First to retrieve the ID.
                {
                    // Set the SelectedRelationship property on the ViewModel
                    vm.SelectedRelationship = relatesLayer.LayerInfo.Relationships.First();
                    // Call Execute method on the ViewModel
                    vm.QueryRelated.Execute(vm.SelectedRelationship);
                }
            }
            else
            {
                return;
            }
        }
        /// <summary>
        /// Handle successful initialization of the resultsLayer
        /// </summary>
        private void resultsLayer_Initialized(object sender, EventArgs e)
        {
            if (LayerProperties.GetPopupTitleExpressions(resultsLayer) == null)
            {
                SetPopupTitleExpression();
            }

            Grid infoWindowGrid = Utils.FindChildOfType <Grid>(popupWindow, 3);

            // Only display the Keep results on map option of the results layer is not a table
            if (!infoWindowGrid.Children.Contains(KeepOnMapView) && resultsLayer.LayerInfo.Type != "Table")
            {
                infoWindowGrid.Children.Insert(1, KeepOnMapView);
            }

            indicator.IsBusy = false;

            // Get the FeatureLayer's OID field from the LayerInfo
            string oidField = resultsLayer.LayerInfo.ObjectIdField;

            // Create a List to hold on to the selected ObjectIds (related records)
            List <int> list = new List <int>();

            //Go through the RelatedRecordsGroup and create a list with the object ids.
            foreach (var records in queryResult.RelatedRecordsGroup)
            {
                foreach (Graphic graphic in records.Value)
                {
                    //  graphic.Geometry.SpatialReference.WKID = map.SpatialReference.WKID;
                    list.Add((int)graphic.Attributes[oidField]);
                }
            }

            // Limit the records displayed to only the ones that match the given object ids.
            resultsLayer.ObjectIDs = list.ToArray();

            // Query is executed and keep on map is still checked (from previous query). Set permanent and temporary layers to null.
            if (KeepOnMap)
            {
                if (permanentLayer != null)
                {
                    permanentLayer = null;
                }

                if (temporaryLayer != null)
                {
                    temporaryLayer = null;
                }

                // Create the permanent layer and add it to the map
                permanentLayer = CreatePermanentLayer();
                map.Layers.Add(permanentLayer);
                ShowMapContents();
            }
            else // Keep on map is unchecked.
            {
                // Create a temp layer first and then wait to see if user clicks checkbox
                if (temporaryLayer != null && map.Layers.Contains(temporaryLayer))
                {
                    map.Layers.Remove(temporaryLayer);
                    temporaryLayer = null;
                }
                temporaryLayer = CreateTempLayer();
                map.Layers.Add(temporaryLayer);
            }
        }
        /// <summary>
        /// Performs the relationship query.
        /// </summary>
        /// <remarks>Called from the doQuery method.</remarks>
        private void QueryRelationship()
        {
            // Set the popupWindow and subscribe to changes on the popupWindow.IsOpen property
            popupWindow = PopupInfo.Container as InfoWindow;

            // Set the attribute grid container and subscripbe to changes on the Visibility property
            attributeGridContainer = MapApplication.Current.FindObjectInLayout(CONTAINER_NAME) as FrameworkElement;
            ExtensionMethods.Properties.NotifyOnDependencyPropertyChanged("Visibility", attributeGridContainer, OnDataGridVisibilityChanged);

            // Listen for selection of a different layer in the map contents.
            MapApplication.Current.SelectedLayerChanged += Current_SelectedLayerChanged;

            // Listen for a change in the selected index of the PopupInfo (indicating the popup item has changed).
            PopupInfo.PropertyChanged += PopupInfo_PropertyChanged;

            // Locate the grid inside the popup window and remove the RelationshipView. This was initially inserted if
            // multiple relationships for a feature were detected.
            Grid infoWindowGrid = Utils.FindChildOfType <Grid>(popupWindow, 3);

            infoWindowGrid.Children.Remove(RelationshipView);

            // Set the relationshipID for the QueryTask.
            int relationshipID = SelectedRelationship.Id;

            // Get the feature and layer info from the pop-up. The PopupItem property of OnClickPopupInfo
            // provides information about the item currently shown in the pop-up.
            Graphic inputFeature = PopupInfo.PopupItem.Graphic;

            relatesLayer = PopupInfo.PopupItem.Layer as FeatureLayer; // The layer to get related records for. This is used to get the RelationshipID and Query url.

            // Get the name of the ObjectID field.
            objectIDField = relatesLayer.LayerInfo.ObjectIdField;

            // Input parameters for QueryTask
            RelationshipParameter relationshipParameters = new RelationshipParameter()
            {
                ObjectIds           = new int[] { (int)inputFeature.Attributes[objectIDField] },
                OutFields           = new string[] { "*" }, // Return all fields
                ReturnGeometry      = true,                 // Return the geometry so that features can be displayed on the map if applicable
                RelationshipId      = relationshipID,       // Obtain the desired RelationshipID from the Service Details page. Here it takes the first relationship it finds if there is more than one.
                OutSpatialReference = map.SpatialReference
            };

            // Specify the Feature Service url for the QueryTask.
            queryTask.Url      = relatesLayer.Url;
            queryTask.ProxyURL = relatesLayer.ProxyUrl;

            // Events for the successful completion of the RelationshipQuery and for if the Query fails
            queryTask.ExecuteRelationshipQueryCompleted += QueryTask_ExecuteRelationshipQueryCompleted;
            queryTask.Failed += QueryTask_Failed;

            // Execute the Query Task with specified parameters
            queryTask.ExecuteRelationshipQueryAsync(relationshipParameters);

            // Create the BusyIndicator and insert into the grid of the popup window.
            indicator             = new BusyIndicator();
            indicator.BusyContent = Strings.RetrievingRecords;
            if (infoWindowGrid != null)
            {
                infoWindowGrid.Children.Add(indicator);
                indicator.IsBusy = true;
            }
        }
예제 #12
0
        }         // public void Execute(object parameter)

        /// <summary>
        /// Executes the relationship query against the layer.
        /// </summary>
        /// <param name="parameter">The OnClickPopupInfo from the layer.</param>
        public void doExecute(object parameter)
        {
            // The plan is:
            // Get the featurelayer and clicked feature from the pop-up.
            // The PopupItem property of OnClickPopupInfo provides information about the item currently shown in the pop-up.
            // Then get feature ID value and put it into ExecuteRelationshipQueryAsync task.
            // Then get related records ID's and create FeatureLayer from related table/feature class, filtered by that ID's.
            // Then show grid for that layer.
            popupInfo    = parameter as OnClickPopupInfo;
            inputFeature = popupInfo.PopupItem.Graphic;
            var lyr = new mwb02.AddIns.VLayer(popupInfo.PopupItem.Layer);

            // print layer info to console
            log(string.Format("Execute, layer type '{0}', popupInd '{1}', popupDescr '{2}', lyrID '{3}', lyrName '{4}', title '{5}'",
                              popupInfo.PopupItem.Layer.GetType(),   // 'ESRI.ArcGIS.Client.ArcGISDynamicMapServiceLayer'
                              popupInfo.SelectedIndex, popupInfo.SelectionDescription,
                              popupInfo.PopupItem.LayerId, popupInfo.PopupItem.LayerName, popupInfo.PopupItem.Title));
            // SelectedIndex - index 0-n for found features.
            // SelectionDescription - note about current record for user, '2 from 2' for example.
            // lyrID '0' - sublayer id for ArcGISDynamicMapServiceLayer
            // lyrName 'Аэродромы и вертодромы' - sublayer name for ArcGISDynamicMapServiceLayer
            log(string.Format("Execute, lyrType '{0}', lyrUrl '{1}'", lyr.lyrType, lyr.lyrUrl));
            // layer type 'ESRI.ArcGIS.Client.ArcGISDynamicMapServiceLayer', popupInd '0', popupDescr '1 из 2', lyrID '0', lyrName 'Wells', title 'UNKNOWN'
            // lyrType 'ArcGISDynamicMapServiceLayer', lyrUrl 'http://sampleserver3.arcgisonline.com/ArcGIS/rest/services/Petroleum/KSPetro/MapServer'
            log(string.Format("Execute, inputFeature.Attributes.Count '{0}'", inputFeature.Attributes.Count));

            // we need FeatureLayer
            if (lyr.lyrType == "FeatureLayer")
            {
                relatesLayer = lyr;                 // The layer to get related records for. This is used to get the RelationshipID and Query url.
            }
            else if (lyr.lyrType == "ArcGISDynamicMapServiceLayer")
            {
                var rLyr = getSubLayer(lyr, popupInfo.PopupItem.LayerId) as FeatureLayer;
                if (relatesLayer != null && relatesLayer.lyrUrl == rLyr.Url)
                {
                    ;                  // we here after relatesLayer.Initialized
                }
                else                   // init new FeatureLayer
                {
                    relatesLayer = new mwb02.AddIns.VLayer(rLyr);
                    relatesLayer.lyr.Initialized += (a, b) => {
                        if (relatesLayer.lyr.InitializationFailure == null)
                        {
                            var info = (relatesLayer.lyr as FeatureLayer).LayerInfo;
                            log(string.Format("Execute, relatesLayer.InitializationFailure == null, info '{0}'", info));
                            Execute(parameter);
                        }
                    };                     // callback
                    relatesLayer.lyr.Initialize();
                    log(string.Format("Execute, relatesLayer.Initialize called, wait..."));
                    return;
                }         // init new FeatureLayer
            }             // if(lyr.lyrType == "ArcGISDynamicMapServiceLayer")
            else
            {
                throw new Exception("Тип слоя должен быть или FeatureLayer или ArcGISDynamicMapServiceLayer");
            }

            // we have inited FeatureLayer now
            if (relatesLayer.getFL().LayerInfo == null)
            {
                throw new Exception(string.Format("Execute, relatesLayer.LayerInfo == null"));
            }
            var clickedLayer = relatesLayer;
            var storedFL     = this.flList[clickedLayer.lyrUrl];

            // check FeatureLayer info
            log(string.Format("Execute, relatesLayer lyrType '{0}', lyrUrl '{1}'", clickedLayer.lyrType, clickedLayer.lyrUrl));

            // get relationship id
            var rels = relatesLayer.getFL().LayerInfo.Relationships;

            log(string.Format("Execute, getrelid.1, rels.count '{0}'", rels.Count()));
            if (rels.Count() <= 0)
            {
                log(string.Format("Execute, relationships.count <= 0"));
                throw new Exception(string.Format("У выбранного слоя нет связей с другими таблицами"));
            }
            else if (rels.Count() > 1)
            {
                log(string.Format("Execute, relationships.count > 1"));
                if (relationsListForm.relationsList.Count > 0)
                {
                    // continue after user input
                    relationInfo = relationsListForm.listBox1.SelectedItem as mwb02.AddIns.VRelationInfo;
                    relationsListForm.relationsList.Clear();
                }                 // user select relID already
                else
                {
                    // new query
                    relationInfo = null;
                    foreach (var r in rels)
                    {
                        var ri = storedFL.getRelation(r);
                        if (ri == null)
                        {
                            ri = new mwb02.AddIns.VRelationInfo(r);
                        }
                        ri.oid = clickedLayer.getOID(inputFeature);
                        if (ri.oid == -1)
                        {
                            log(string.Format("featurelayer.getOID returns invalid OID; {0}", clickedLayer.lyrUrl));
                            continue;
                        }
                        relationsListForm.relationsList.Add(ri);
                    }
                    relationsListForm.listBox1.SelectedItem = relationsListForm.relationsList.First();
                    MapApplication.Current.ShowWindow("Relations",
                                                      relationsListForm,
                                                      false,                                                                      // ismodal
                                                      (sender, canceleventargs) => { log("relationsListForm onhidINGhandler"); }, // onhidinghandler
                                                      (sender, eventargs) => {
                        log("relationsListForm onhidEhandler");
                        //if(relationsListForm.listBox1.SelectedItem != null)
                        Execute(parameter);
                    },                             // onhidehandler
                                                      WindowType.Floating
                                                      );
                    return; // wait for user input
                }           // new query
            }               // rels.count > 1
            else            // rels.count == 1
            {
                log(string.Format("Execute, relationships.count = 1"));
                relationInfo = new mwb02.AddIns.VRelationInfo(rels.First());
            }

            // ok, we get relation info now
            if (relationInfo == null)
            {
                throw new Exception("Не указана связанная таблица");
            }
            log(string.Format("Execute, getrelid.2, relationshipID '{0}', rels.count '{1}'", relationInfo.id, rels.Count()));

            // Get the name of the ObjectID field.
            objectID = clickedLayer.getOIDFieldnameOrAlias();

            // get key value
            int objIdValue = clickedLayer.getOID(inputFeature);

            if (objIdValue == -1)
            {
                // Attributes key = 'Object ID' but ObjectIdField = 'OBJECTID'
                var ks = string.Join(", ", inputFeature.Attributes.Keys);                 // inputFeature.AttributesKeys='Object ID, Shape, Field KID,
                var vs = string.Join(", ", inputFeature.Attributes.Values);
                log(string.Format("Execute, inputFeature.AttributesKeys='{0}', values='{1}'", ks, vs));
                throw new Exception(string.Format("Поле OBJECTID не содержит целого числа"));
            }
            log(string.Format("Execute, objIdValue.int='{0}'", objIdValue));

            // Input parameters for QueryTask
            RelationshipParameter relationshipParameters = new RelationshipParameter()
            {
                ObjectIds           = new int[] { objIdValue },
                OutFields           = new string[] { "*" }, // Return all fields
                ReturnGeometry      = true,                 // Return the geometry so that features can be displayed on the map if applicable
                RelationshipId      = relationInfo.id,      // Obtain the desired RelationshipID from the Service Details page. Here it takes the first relationship it finds if there is more than one.
                OutSpatialReference = MapApplication.Current.Map.SpatialReference
            };

            log(string.Format("Execute, relationshipParameters set"));

            // Specify the Feature Service url for the QueryTask.
            if (queryTask.IsBusy)
            {
                throw new Exception("Выполняется предыдущий запрос, попробуйте позже");
            }
            queryTask.Url = relatesLayer.lyrUrl;

            //  Execute the Query Task with specified parameters
            queryTask.ExecuteRelationshipQueryAsync(relationshipParameters);

            // Find the attribute grid in the Pop-up and insert the BusyIndicator
            attributeGrid = Utils.FindChildOfType <Grid>(popupInfo.AttributeContainer, 3);
            indicator     = new BusyIndicator();
            if (attributeGrid != null)
            {
                // Add the Busy Indicator
                attributeGrid.Children.Add(indicator);
                indicator.IsBusy = true;
            }

            log(string.Format("Execute, completed, wait for QueryTask_ExecuteRelationshipQueryCompleted"));
        }         // public void doExecute(object parameter)
        } // public void Execute(object parameter)

        /// <summary>
        /// Executes the relationship query against the layer.
        /// </summary>
        /// <param name="parameter">The OnClickPopupInfo from the layer.</param>
        public void doExecute(object parameter)
        {
            // The plan is:
            // Get the featurelayer and clicked feature from the pop-up.
            // The PopupItem property of OnClickPopupInfo provides information
            // about the item currently shown in the pop-up.
            // Then get feature ID value and put it into ExecuteRelationshipQueryAsync task.
            // Then get related records ID's and create FeatureLayer from
            // related table/feature class, filtered by that ID's.
            // Then show grid for that layer.
            popupInfo    = parameter as OnClickPopupInfo;
            inputFeature = popupInfo.PopupItem.Graphic;
            var lyr = new VUtils.ArcGIS.SLViewer.VLayer(popupInfo.PopupItem.Layer);

            // print layer info to console
            log(string.Format(
                    "Execute, layer type '{0}', popupInd '{1}', popupDescr '{2}', lyrID '{3}', lyrName '{4}', title '{5}'",
                    popupInfo.PopupItem.Layer.GetType(),
                    popupInfo.SelectedIndex, popupInfo.SelectionDescription,
                    popupInfo.PopupItem.LayerId, popupInfo.PopupItem.LayerName, popupInfo.PopupItem.Title));
            log(string.Format("Execute, lyrType '{0}', lyrUrl '{1}'", lyr.lyrType, lyr.lyrUrl));
            log(string.Format("Execute, inputFeature.Attributes.Count '{0}'", inputFeature.Attributes.Count));

            // we need FeatureLayer
            if (lyr.lyrType == "FeatureLayer")
            {
                // The layer to get related records for.
                // This is used to get the RelationshipID and Query url.
                relatesLayer = lyr.lyr as FeatureLayer;
            }
            else if (lyr.lyrType == "ArcGISDynamicMapServiceLayer")
            {
                var rLyr = getSubLayer(lyr, popupInfo.PopupItem.LayerId) as FeatureLayer;
                if (relatesLayer != null && relatesLayer.Url == rLyr.Url)
                {
                    // we're here after relatesLayer.Initialized
                    ;
                }
                else
                {
                    // init new FeatureLayer
                    relatesLayer              = rLyr;
                    relatesLayer.Initialized += (a, b) => {
                        if (relatesLayer.InitializationFailure == null)
                        {
                            var info = relatesLayer.LayerInfo;
                            log(string.Format(
                                    "Execute, relatesLayer.InitializationFailure == null, info '{0}'",
                                    info));
                            Execute(parameter);
                        }
                    }; // callback
                    relatesLayer.Initialize();
                    log(string.Format("Execute, relatesLayer.Initialize called, wait..."));
                    return;
                } // init new FeatureLayer
            }     // if(lyr.lyrType == "ArcGISDynamicMapServiceLayer")
            else
            {
                throw new Exception("Layer type must be FeatureLayer or ArcGISDynamicMapServiceLayer");
            }

            // we have inited FeatureLayer now
            if (relatesLayer.LayerInfo == null)
            {
                throw new Exception(string.Format("Execute, relatesLayer.LayerInfo == null"));
            }
            var clickedLayer = new VUtils.ArcGIS.SLViewer.VLayer(relatesLayer);

            // check FeatureLayer info
            log(string.Format(
                    "Execute, relatesLayer lyrType '{0}', lyrUrl '{1}'",
                    clickedLayer.lyrType, clickedLayer.lyrUrl));

            // get relationship id
            var rels = relatesLayer.LayerInfo.Relationships;

            if (rels.Count() <= 0)
            {
                log(string.Format("Execute, relationships.count <= 0"));
                throw new Exception(string.Format("Layer have not relations"));
            }
            else if (rels.Count() > 1)
            {
                log(string.Format("Execute, relationships.count > 1"));
                if (relationsListForm.listBox1.Items.Count > 0)
                {
                    // continue after user input
                    // user selected relID already
                    relationInfo = new VUtils.ArcGIS.SLViewer.VRelationInfo(
                        relationsListForm.listBox1.SelectedItem as string);
                    relationsListForm.listBox1.Items.Clear();
                }
                else
                {
                    // new query
                    foreach (var r in rels)
                    {
                        var ri = new VUtils.ArcGIS.SLViewer.VRelationInfo(r);
                        relationsListForm.listBox1.Items.Add(ri.descr);
                    }
                    relationsListForm.listBox1.SelectedItem = relationsListForm.listBox1.Items.First();
                    MapApplication.Current.ShowWindow("Relations",
                                                      relationsListForm,
                                                      false, // ismodal
                                                      (sender, canceleventargs) => {
                        log("relationsListForm onhidINGhandler");
                    },     // onhidinghandler
                                                      (sender, eventargs) => {
                        log("relationsListForm onhidEhandler");
                        if (relationsListForm.listBox1.SelectedItem != null)
                        {
                            Execute(parameter);
                        }
                    },     // onhidehandler
                                                      WindowType.Floating
                                                      );
                    return; // wait for user input
                } // new query
            } // rels.count > 1
            else   // rels.count == 1
            {
                log(string.Format("Execute, relationships.count = 1"));
                relationInfo = new VUtils.ArcGIS.SLViewer.VRelationInfo(rels.First());
            }

            // ok, we get relation info now
            log(string.Format(
                    "Execute, getrelid, relationshipID '{0}', rels.count '{1}'",
                    relationInfo.id, rels.Count()));

            // Get the name of the ObjectID field.
            objectID = relatesLayer.LayerInfo.ObjectIdField;
            string objectIDAlias = clickedLayer.getFieldAlias(objectID);

            log(string.Format("Execute, objectID '{0}', alias '{1}'", objectID, objectIDAlias));
            if (objectIDAlias != "")
            {
                objectID = objectIDAlias;                     // because of bug? in Graphic.Attributes[fieldname]
            }
            // get key value
            Object v = null;

            v = inputFeature.Attributes[objectID];
            log(string.Format("Execute, objIdValue.str='{0}'", v));
            int objIdValue = -1;

            try {
                objIdValue = Int32.Parse(string.Format("{0}", v));
            }
            catch (Exception ex) {
                // fieldname = 'OBJECTID' but alias = 'Object ID'
                var ks = string.Join(", ", inputFeature.Attributes.Keys);
                var vs = string.Join(", ", inputFeature.Attributes.Values);
                log(string.Format("Execute, inputFeature.AttributesKeys='{0}', values='{1}'", ks, vs));
                throw new Exception(string.Format("OBJECTID is not an integer"));
            }
            log(string.Format("Execute, objIdValue.int='{0}'", objIdValue));

            // Input parameters for QueryTask
            RelationshipParameter relationshipParameters = new RelationshipParameter()
            {
                ObjectIds      = new int[] { objIdValue },
                OutFields      = new string[] { "*" }, // Return all fields
                ReturnGeometry = true,                 // Return the geometry
                // so that features can be displayed on the map if applicable

                RelationshipId = relationInfo.id, // Obtain the desired RelationshipID
                // from the Service Details page. Here it takes the first relationship
                // it finds if there is more than one.

                OutSpatialReference = MapApplication.Current.Map.SpatialReference
            };

            // Specify the Feature Service url for the QueryTask.
            queryTask.Url = relatesLayer.Url;

            //  Execute the Query Task with specified parameters
            queryTask.ExecuteRelationshipQueryAsync(relationshipParameters);

            // Find the attribute grid in the Pop-up and insert the BusyIndicator
            attributeGrid = Utils.FindChildOfType <Grid>(popupInfo.AttributeContainer, 3);
            indicator     = new BusyIndicator();
            if (attributeGrid != null)
            {
                // Add the Busy Indicator
                attributeGrid.Children.Add(indicator);
                indicator.IsBusy = true;
            }

            log(string.Format("Execute, completed, wait for QueryTask_ExecuteRelationshipQueryCompleted"));
        } // public void doExecute(object parameter)