Пример #1
0
        public event EventHandler CanExecuteChanged; // Event that can be raised when the executability of the command changes. When this event is raised, the Viewer will invoke the CanExecute method, allowing the add-in to update the state of the tool's button on the toolbar.
        //if (CanExecuteChanged != null){CanExecuteChanged(this, EventArgs.Empty);}

        #endregion ICommand members


        #region Event Handlers
        /// <summary>
        /// Handle successful query task and create FeatureLayer from related table/FC
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void QueryTask_ExecuteRelationshipQueryCompleted(object sender, RelationshipEventArgs e)
        {
            // queryResult used once the results layer is initialized
            queryResult = e.Result;

            // Create a new url for the related table using the querytaskUrl and the RelatedTableId.
            string resultsUrl = relatesLayer.lyrUrl;
            var    lastSlash  = resultsUrl.LastIndexOf("/");         // -1?

            resultsUrl = string.Format("{0}/{1}", resultsUrl.Substring(0, lastSlash), relationInfo.tableId);
            log(string.Format("QueryTask_ExecuteRelationshipQueryCompleted, resultsUrl='{0}'", resultsUrl));

            // Create a FeatureLayer for the results based on the url of the related records.
            var resFL = new FeatureLayer()
            {
                Url = resultsUrl
            };

            resFL.OutFields.Add("*");

            // Initialize the resultsLayer to populate layer metadata (LayerInfo) so the OID field can be retrieved.
            resFL.Initialized += resultsLayer_Initialized;
            resFL.Initialize();
            resultsLayer = new mwb02.AddIns.VLayer(resFL);

            log(string.Format("QueryTask_ExecuteRelationshipQueryCompleted, completed, wait for resultsLayer_Initialized"));
        }         // private void QueryTask_ExecuteRelationshipQueryCompleted(object sender, RelationshipEventArgs e)
Пример #2
0
        }         // public void doExecute(object parameter)

        /// <summary>
        /// Checks whether the Query Related Records tool can be used.
        /// </summary>
        /// <param name="parameter">The OnClickPopupInfo from the layer.</param>
        public bool CanExecute(object parameter)
        {
            try {
                popupInfo = parameter as OnClickPopupInfo;
                if (popupInfo == null || popupInfo.PopupItem == null || popupInfo.PopupItem.Graphic == null)
                {
                    //log(string.Format("CanExecute false, popupinfo or popupitem is null"));
                    return(false);
                }

                if (MapApplication.Current == null || MapApplication.Current.Map == null ||
                    MapApplication.Current.Map.Layers == null)
                {
                    //log(string.Format("CanExecute false, map or layers is null"));
                    return(false);
                }

                var lyr = new mwb02.AddIns.VLayer(popupInfo.PopupItem.Layer);
                if (lyr.lyrType.Contains("DynamicMapServiceLayer"))
                {
                    lyr = lyr.getSubLayer(popupInfo.PopupItem.LayerId);
                }
                if (!lyr.lyrType.Contains("Feature"))
                {
                    log(string.Format("CanExecute false, layer is not FeatureLayer, {0}", lyr.lyrType));
                    return(false);
                }

                // http://www.dotnetperls.com/dictionary
                var vfl = new mwb02.AddIns.VLayer();
                if (!flList.TryGetValue(lyr.lyrUrl, out vfl))
                {
                    log(string.Format("CanExecute false, layer not in list {0}", lyr.lyrUrl));
                    addFL2Collection(lyr);
                    return(false);
                }

                var fl = vfl.lyr as FeatureLayer;
                if (fl.LayerInfo != null && fl.LayerInfo.Relationships != null && fl.LayerInfo.Relationships.Count() > 0)
                {
                    var relrecs = countRelatedRecords(vfl, popupInfo.PopupItem.Graphic);
                    if (relrecs != 0)
                    {
                        return(true);
                    }
                    log(string.Format("CanExecute false, relatedrecords.count = 0. {0}", lyr.lyrUrl));
                    return(false);
                }

                log(string.Format("CanExecute false, layerinfo or relations is null {0}", lyr.lyrUrl));
                return(false);
            }
            catch (Exception ex) {
                log(string.Format("CanExecute error {0}\n{1}", ex.Message, ex.StackTrace));
                return(false);
            }
        }                                            // public bool CanExecute(object parameter)
Пример #3
0
        }         // private void askServerForRelatedRecs(mwb02.AddIns.VLayer vfl, ESRI.ArcGIS.Client.Graphic feature)

        /// <summary>
        /// Return FeatureLayer from ArcGISDynamicMapServiceLayer by lyrID
        /// </summary>
        /// <param name="lyr"></param>
        /// <param name="lyrID"></param>
        /// <returns></returns>
        private Layer getSubLayer(mwb02.AddIns.VLayer lyr, int lyrID)
        {
            var fl = lyr.getSubLayer(lyrID);

            if (fl == null)
            {
                throw new Exception("Не удалось вынуть FeatureLayer из ArcGISDynamicMapServiceLayer");
            }
            return(fl.lyr);
        }         // private Layer getSubLayer(mwb02.AddIns.VLayer lyr, int lyrID)
Пример #4
0
        }         // private void getFLsFromDynamapservicelyr(mwb02.AddIns.VLayer lyr)

        /// <summary>
        /// initialize featurelayer if it's not, add FL to list
        /// </summary>
        /// <param name="lyr"></param>
        private void addFL2Collection(mwb02.AddIns.VLayer vfl)
        {
            if (vfl.lyrType == "FeatureLayer")
            {
                var featurelayer = vfl.lyr as FeatureLayer;
                if (featurelayer.LayerInfo == null)
                {
                    log(string.Format("addFL2Collection, FL not inited {0}", vfl.lyrUrl));
                    //featurelayer.Initialize();

                    // call init sequence
                    featurelayer.Initialized += (sender, eventargs) => {
                        if (featurelayer.InitializationFailure == null)
                        {
                            var lyrinfo = featurelayer.LayerInfo;
                            if (lyrinfo == null)
                            {
                                log(string.Format("addFL2Collection, Initialize did't work, FL info is null, {0}", vfl.lyrUrl));
                            }
                            else                               // do the job
                            {
                                log(string.Format("addFL2Collection, Initialize, FL inited, {0}", vfl.lyrUrl));
                                addFL2Collection(vfl);
                            }
                        }                         // init ok
                        else                      // init error
                        {
                            log(string.Format("addFL2Collection, Initialize, error, lyr (0), msg {3}",
                                              vfl.lyrUrl, featurelayer.InitializationFailure.Message));
                        }
                    };            // lyr initialized callback
                    featurelayer.Initialize();
                }                 // if(featurelayer.LayerInfo == null)
                else              // FL inited
                {
                    if (!flList.ContainsKey(vfl.lyrUrl))
                    {
                        vfl.initRelations();
                        flList.Add(vfl.lyrUrl, vfl);
                        log(string.Format("addFL2Collection, FLs count {2}, FL {0} relations {1}", vfl.lyrUrl, featurelayer.LayerInfo.Relationships.Count(), flList.Count));
                        if (CanExecuteChanged != null)
                        {
                            CanExecuteChanged(this, EventArgs.Empty);
                        }
                    }
                }
            }             // if FeatureLayer
            else
            {
                log(string.Format("addFL2Collection, lyrtype != FeatureLayer, {0}", vfl.lyrUrl));
            }
        }         // private void addFL2Collection(mwb02.AddIns.VLayer lyr)
Пример #5
0
        }         // public bool getBoolFromJson(JsonObject js, string key)

        /// <summary>
        /// Return FeatureLayer or null from ArcGISDynamicMapServiceLayer by lyrID
        /// </summary>
        /// <param name="lyrID">ArcGISDynamicMapServiceLayer sublayer id</param>
        /// <returns>VLayer with FeatureLayer inside</returns>
        public VLayer getSubLayer(int lyrID)
        {
            if (this.lyrType == "ArcGISDynamicMapServiceLayer")
            {
                var ld = new mwb02.AddIns.VLayerDescription();
                ld.type  = "FeatureLayer";
                ld.url   = string.Format("{0}/{1}", this.lyrUrl, lyrID);
                ld.proxy = this.proxy;
                var res = new mwb02.AddIns.VLayer(ld);
                return(res);
            }
            else
            {
                return(null);
            }
        }         // private Layer getSubLayer(mwb02.AddIns.VLayer lyr, int lyrID)
Пример #6
0
        }         // void Layers_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)

        /// <summary>
        /// get list of featurelayers from ArcGISDynamicMapServiceLayer
        /// </summary>
        /// <param name="lyr"></param>
        /// <returns></returns>
        private void getFLsFromDynamapservicelyr(mwb02.AddIns.VLayer lyr)
        {
            if (lyr.lyrType == "ArcGISDynamicMapServiceLayer")
            {
                var dl = lyr.lyr as ArcGISDynamicMapServiceLayer;

                dl.GetAllDetails(
                    (layeritems, ex) => {
                    if (ex == null)
                    {
                        log(string.Format("getFLsFromDynamapservicelyr, GetAllDetails, lyr {0}, items {1}", lyr.lyrUrl, layeritems.Count));
                        foreach (var kvp in layeritems)
                        {
                            var fli = kvp.Value;                                     //FeatureLayerInfo
                            log(string.Format("getFLsFromDynamapservicelyr, GetAllDetails, lyrid {0}, lyrname {1}, lyrtype {2}",
                                              fli.Id, fli.Name, fli.Type));

                            if (fli.Type.Contains("Feature"))
                            {
                                var vfl = lyr.getSubLayer(fli.Id);
                                if (vfl == null)
                                {
                                    log(string.Format("getFLsFromDynamapservicelyr, GetAllDetails, vfl=null"));
                                }
                                else
                                {
                                    addFL2Collection(vfl);
                                }
                            }                     // if sublayer is FeatureLayer
                        }                         // foreach layerinfo
                    }                             // no errors on init
                    else
                    {
                        log(string.Format("getFLsFromDynamapservicelyr, GetAllDetails fail, lyr {0}, msg '{1}'",
                                          lyr.lyrUrl, ex.Message));
                    }
                });       // getalldetails callback
            }             // if ArcGISDynamicMapServiceLayer
            else
            {
                log(string.Format("getFLsFromDynamapservicelyr, lyrtype != ArcGISDynamicMapServiceLayer, {0}", lyr.lyrType));
            }
            return;
        }         // private void getFLsFromDynamapservicelyr(mwb02.AddIns.VLayer lyr)
Пример #7
0
        }         // public QueryRelatedTool()

        #region layers collection

        void Layers_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            try {
                if (e.NewItems == null)
                {
                    return;
                }

                log(string.Format("Layers_CollectionChanged, new items {0}", e.NewItems.Count));
                foreach (var i in e.NewItems)
                {
                    log(string.Format("Layers_CollectionChanged, new layer type '{0}'", i.GetType()));                     // 'ESRI.ArcGIS.Client.GraphicsLayer'
                    // extract featurelayers, init them and add to layerslist for further needs
                    var vl = new mwb02.AddIns.VLayer(i as Layer);
                    //log(string.Format("Layers_CollectionChanged, new layer JSON '{0}'", vl.toJson()));
                    log(string.Format("Layers_CollectionChanged, new layer details '{0}' '{1}'", vl.ID, vl.lyrName));

                    if (vl.lyrType == "FeatureLayer")
                    {
                        log(string.Format("Layers_CollectionChanged, featurelayer added"));
                        addFL2Collection(vl);
                    }
                    else if (vl.lyrType == "ArcGISDynamicMapServiceLayer")
                    {
                        log(string.Format("Layers_CollectionChanged, dynamapservicelayer added, need to extract FLs"));
                        getFLsFromDynamapservicelyr(vl);
                    }
                    // todo: else if(vl.lyrType == "ArcGISTiledMapServiceLayer") {}
                    else
                    {
                        log(string.Format("Layers_CollectionChanged, unsupported layer"));
                    }
                }                 // foreach added layer
            }
            catch (Exception ex) {
                log(string.Format("Layers_CollectionChanged, error {0}", ex.Message));
            }
        }         // void Layers_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
Пример #8
0
        }         // countRelatedRecords

        /// <summary>
        /// ask related records from server for each relatioin, one relation at a time
        /// </summary>
        /// <param name="vfl"></param>
        /// <param name="feature"></param>
        private void askServerForRelatedRecs(mwb02.AddIns.VLayer vfl, ESRI.ArcGIS.Client.Graphic feature)
        {
            var fl   = vfl.lyr as FeatureLayer;
            var rels = fl.LayerInfo.Relationships;
            // Get the name of the ObjectID field.
            var oidFieldname = vfl.getOIDFieldnameOrAlias();
            // get key value
            int objIdValue = vfl.getOID(feature);

            log(string.Format("askServerForRelatedRecs, ready for ask, {0}/{1}", vfl.lyrUrl, objIdValue));

            //for each relation
            if (vfl.relations == null)
            {
                vfl.initRelations();
            }
            foreach (var rel in rels)
            {
                var relinfo = vfl.getRelation(rel);
                if (relinfo == null)
                {
                    throw new Exception(string.Format("askServerForRelatedRecs, stored relinfo is null, make it; {0}/{1}, rel {2}",
                                                      vfl.lyrUrl, objIdValue, rel.Name));
                }
                log(string.Format("askServerForRelatedRecs, have relinfo {0}/{1}, rel {2}", vfl.lyrUrl, objIdValue, relinfo.name));

                if (relinfo.linkedRecords == null)
                {
                    log(string.Format("askServerForRelatedRecs, create linkedRecords dict {0}/{1}, rel {2}", vfl.lyrUrl, objIdValue, relinfo.name));
                    relinfo.linkedRecords = new Dictionary <int, IEnumerable <Graphic> >();
                }
                IEnumerable <Graphic> recs;
                if (relinfo.linkedRecords.TryGetValue(objIdValue, out recs))
                {
                    log(string.Format("askServerForRelatedRecs, have relrecs for feature already, skip; {0}/{1}, rel {2}, count {3}",
                                      vfl.lyrUrl, objIdValue, relinfo.name, recs == null ? 0 : recs.Count()));
                    continue;                     // goto next relation
                }

                log(string.Format("askServerForRelatedRecs, haven't relrecs for feature, get it! {0}/{1}, rel {2}", vfl.lyrUrl, objIdValue, relinfo.name));
                // get data from server. then store it
                var qt = new QueryTask();

                // callbacks
                qt.Failed += (sender, eventargs) => {
                    this.isBusy = false;
                    log(string.Format("askServerForRelatedRecs, QueryTask.Failed! {0}/{1}, rel {2}", vfl.lyrUrl, objIdValue, relinfo.name));
                    relinfo.linkedRecords.Add(objIdValue, null);
                    if (CanExecuteChanged != null)
                    {
                        CanExecuteChanged(this, EventArgs.Empty);
                    }
                };                 // qt.failed

                qt.ExecuteRelationshipQueryCompleted += (sender, eventargs) => {
                    this.isBusy = false;
                    log(string.Format("askServerForRelatedRecs, QueryTask.Completed, {0}/{1}, rel {2}", vfl.lyrUrl, objIdValue, relinfo.name));
                    var qres = eventargs.Result;
                    recs = null;
                    foreach (var records in qres.RelatedRecordsGroup)
                    {
                        var graphics = records.Value;
                        if (recs == null)
                        {
                            recs = graphics;
                        }
                        else
                        {
                            recs = recs.Concat(graphics);
                        }
                    }
                    log(string.Format("askServerForRelatedRecs, QueryTask.Completed, {0}/{1}, rel {2}, recs {3}",
                                      vfl.lyrUrl, objIdValue, relinfo.name, recs == null ? 0 : recs.Count()));
                    // store data, repeat for next relation
                    relinfo.linkedRecords.Add(objIdValue, recs);
                    if (CanExecuteChanged != null)
                    {
                        CanExecuteChanged(this, EventArgs.Empty);
                    }
                };                 // qt.ExecuteRelationshipQueryCompleted

                // Input parameters for QueryTask
                if (!qt.IsBusy)
                {
                    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      = relinfo.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
                    };
                    qt.Url      = fl.Url;
                    this.isBusy = true;
                    log(string.Format("askServerForRelatedRecs, QueryTask.Execute, {0}/{1}, rel {2}", vfl.lyrUrl, objIdValue, relinfo.name));

                    qt.ExecuteRelationshipQueryAsync(relationshipParameters);
                    return;
                }
            }     // for each relation
        }         // private void askServerForRelatedRecs(mwb02.AddIns.VLayer vfl, ESRI.ArcGIS.Client.Graphic feature)
Пример #9
0
        /// <summary>
        /// found related records for each relation for selected feature;
        /// -1 means unknown number of related records
        /// </summary>
        /// <param name="vfl"></param>
        /// <returns></returns>
        private int countRelatedRecords(mwb02.AddIns.VLayer vfl, ESRI.ArcGIS.Client.Graphic feature)
        {
            int allLinkedRecordsCount = -1; // unknown number

            if (this.isBusy)                // wait for previous request completion
            //log(string.Format("countRelatedRecords.isBusy; lyr {0}", vfl.lyrUrl));
            {
                return(allLinkedRecordsCount);
            }

            // get layer from storage
            mwb02.AddIns.VLayer storedLyr = null;
            if (!this.flList.TryGetValue(vfl.lyrUrl, out storedLyr))
            {
                log(string.Format("countRelatedRecords, not in storage, lyr {0}", vfl.lyrUrl));
                storedLyr = vfl;
                this.flList.Add(vfl.lyrUrl, storedLyr);
            }
            vfl = storedLyr;

            // Get the name of the ObjectID field.
            var oidFieldname = vfl.getOIDFieldnameOrAlias();
            // get key value
            int objIdValue = vfl.getOID(feature);

            if (vfl.relations == null)
            {
                vfl.initRelations();
            }
            //log(string.Format("countRelatedRecords, lyr {0}, keyField {1}, key {2}, rels.count {3}", vfl.lyrUrl, oidFieldname, objIdValue, vfl.relations.Count));

            // count rel.records using stored info
            allLinkedRecordsCount = 0;
            foreach (var rel in vfl.relations)
            {
                if (rel.linkedRecords == null)
                {
                    allLinkedRecordsCount = -1;
                    log(string.Format("countRelatedRecords, rel.linkedRecords=null, lyr {0}, key {1}, rel {2}", vfl.lyrUrl, objIdValue, rel.name));
                    break;                     // need get data from server
                }
                else
                {
                    IEnumerable <Graphic> recs;                    // feature related records
                    if (rel.linkedRecords.TryGetValue(objIdValue, out recs))
                    {
                        //log(string.Format("countRelatedRecords, have stored data for feature {0}/{1}, rel {2}, relrecs {3}",vfl.lyrUrl, objIdValue, rel.name, recs==null?0:recs.Count()));
                        if (recs != null)
                        {
                            allLinkedRecordsCount += recs.Count();
                        }
                    }
                    else
                    {
                        allLinkedRecordsCount = -1;
                        log(string.Format("countRelatedRecords, no stored data for feature {0}/{1}, rel {2}", vfl.lyrUrl, objIdValue, rel.name));
                        break;                         // need get data from server
                    }
                }
            }
            // stored info exists for this layer and this feature
            if (allLinkedRecordsCount != -1)
            {
                //log(string.Format("countRelatedRecords, have count from stored data, {0}/{1}, allrecs {2}", vfl.lyrUrl, objIdValue, allLinkedRecordsCount));
                return(allLinkedRecordsCount);
            }

            // otherwise we should get data from server
            log(string.Format("countRelatedRecords, ask server for relrecs, {0}/{1}", vfl.lyrUrl, objIdValue));
            askServerForRelatedRecs(vfl, feature);
            log(string.Format("countRelatedRecords, wait for answer, {0}/{1}", vfl.lyrUrl, objIdValue));
            return(allLinkedRecordsCount);
        }         // countRelatedRecords
Пример #10
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)