protected virtual void OnPopupItemChanged(PopupItem oldPopupItem, PopupItem newPopupItem)
 {
     RefreshCommand();
 }
 public static bool CanEditShape(PopupItem popup, ESRI.ArcGIS.Client.Editor editor = null)
 {
     if (editor == null)
         editor = FindEditorInVisualTree();
     var featureLayer = popup.Layer as FeatureLayer;
     if (popup == null
     || popup.Graphic == null
     || featureLayer == null
     || featureLayer.LayerInfo == null
     || !featureLayer.IsGeometryUpdateAllowed(popup.Graphic)
     || !(LayerProperties.GetIsEditable(featureLayer))
     || editor == null
     || editor.EditVertices == null
     || !editor.EditVerticesEnabled
     || View.Instance.Map == null) return false;
     
     return true;
 }
        public static object GetFieldValue(PopupItem popupItem, string fieldName)
        {
            if (string.IsNullOrEmpty(fieldName) || popupItem == null || popupItem.Graphic == null || popupItem.Graphic.Attributes == null
                || popupItem.FieldInfos == null)
                return null;

            Layer layer = popupItem.Layer;
            FeatureLayer featureLayer = layer as FeatureLayer;
            Collection<LayerInformation> layerInfos = ESRI.ArcGIS.Mapping.Core.LayerExtensions.GetLayerInfos(layer);

            // Try to find feature layer from layer info
            foreach (LayerInformation item in layerInfos)
            {
                if (item.ID == popupItem.LayerId)
                {
                    featureLayer = item.FeatureLayer;
                    break;
                }
            }

            // Try to find field information from field settings collection (which are FieldInfo objects)
            FieldInfo fldInfo = null;
            foreach (FieldSettings fld in popupItem.FieldInfos)
            {
                if (fld.Name.ToLower().Equals(fieldName.ToLower()))
                {
                    fldInfo = fld as FieldInfo;
                    break;
                }
            }

            if (fldInfo == null)
                return null;

            // Create local variable for attributes to improve performance since collection is referenced multiple
            // times in logic below.
            IDictionary<string, object> attributes = popupItem.Graphic.Attributes;

            // If the field is a subdomain type, then lookup the value and return
            if (FieldInfo.GetDomainSubTypeLookup(featureLayer, fldInfo) != DomainSubtypeLookup.None)
                return GetDomainValue(featureLayer, attributes, fldInfo);

            // Try to extract value from field using Name and then DisplayName
            object fieldValue = null;
            if (!attributes.TryGetValue(fldInfo.Name, out fieldValue))
            {
                if (!attributes.TryGetValue(fldInfo.DisplayName, out fieldValue))
                    return null;
            }

            // Special formatting if the field type is Currency or Date/Time
            if (fldInfo.FieldType == FieldType.Currency)
            {
                CurrencyFieldValue currencyFieldValue = fieldValue as CurrencyFieldValue;
                if (currencyFieldValue != null)
                    fieldValue = currencyFieldValue.FormattedValue;
            }
            else if (fldInfo.FieldType == FieldType.DateTime)
            {
                DateTimeFieldValue dateFieldValue = fieldValue as DateTimeFieldValue;
                if (dateFieldValue != null)
                    fieldValue = dateFieldValue.FormattedValue;
            }

            return fieldValue;
        }
		public static DataTemplate BuildMapTipDataTemplate(PopupItem popupItem, out bool hasContent, LayerInformation layerInfo = null)
		{
			hasContent = false;
            if (popupItem == null || popupItem.Graphic == null || popupItem.Graphic.Attributes == null || popupItem.Layer == null)
                return null;

            Layer layer = popupItem.Layer;
			string popupDataTemplatesXaml = null;
            #region Get from developer override/webmap override
            //First get from developer override
            IDictionary<int, string> templates = ESRI.ArcGIS.Client.Extensibility.LayerProperties.GetPopupDataTemplates(layer);
            //then if defined in web map
            if (templates == null && ESRI.ArcGIS.Mapping.Core.LayerExtensions.GetUsePopupFromWebMap(layer))
                templates = ESRI.ArcGIS.Mapping.Core.LayerExtensions.GetWebMapPopupDataTemplates(layer);
            if (templates != null)
            {
                if (layer is GraphicsLayer)
                    popupDataTemplatesXaml = (templates.ContainsKey(-1)) ? templates[-1] : null;
                else
                    popupDataTemplatesXaml = (templates.ContainsKey(layerInfo.ID)) ? templates[layerInfo.ID] : null;
            }
            #endregion

			DataTemplate dt = null;
			StringBuilder sb = new StringBuilder();
            if (!string.IsNullOrEmpty(popupDataTemplatesXaml))
            {
                // Use popup data template xaml.
                hasContent = true;
                sb.Append(popupDataTemplatesXaml);
            }
            else
            {
                sb.Append("<DataTemplate xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\" ");
                sb.Append("xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" ");
                sb.Append("xmlns:mapping=\"http://schemas.esri.com/arcgis/mapping/2009\" ");
                sb.Append("xmlns:local=\"clr-namespace:ESRI.ArcGIS.Mapping.Controls;assembly=ESRI.ArcGIS.Mapping.Controls\" >");
                sb.Append("<Grid HorizontalAlignment=\"Stretch\"><Grid.Resources>");
                sb.Append("<mapping:LabelAttributeConverter x:Key=\"LabelAttributeConverter\" />");
                sb.Append("<mapping:UrlLocationAttributeConverter x:Key=\"UrlLocationAttributeConverter\" />");
                sb.Append("<mapping:UrlDescriptionAttributeConverter x:Key=\"UrlDescriptionAttributeConverter\" />");
                sb.Append("</Grid.Resources>");

                if (popupItem.FieldInfos != null)
                {
                    // Grid with three columns:
                    sb.Append("<Grid.ColumnDefinitions>");
                    sb.Append("<ColumnDefinition Width=\"2*\"/>");
                    sb.Append("<ColumnDefinition Width=\"Auto\"/>");
                    sb.Append("<ColumnDefinition Width=\"3*\" />");
                    sb.Append("</Grid.ColumnDefinitions>");

                    string gridRowDefinitions = string.Empty;
                    StringBuilder content = new StringBuilder();
                    int numRows = 0;
                    foreach (FieldSettings field in popupItem.FieldInfos)
                    {
                        // If field is not to be displayed, skip this field
                        if (!field.VisibleOnMapTip)
                            continue;

                        hasContent = true;
                        gridRowDefinitions += "<RowDefinition Height=\"Auto\" />";
                        numRows++;

                        // Apply alternating row background to every other row
                        if (numRows % 2 == 0)
                        {
                            content.AppendFormat(@"<Rectangle Fill=""{{StaticResource BackgroundTextColorBrush}}""
                                                HorizontalAlignment=""Stretch"" VerticalAlignment=""Stretch""
                                                Grid.ColumnSpan=""3"" Grid.Row=""{0}"" Opacity=""0.15"" />", numRows - 1);
                        }

                        if (!string.IsNullOrEmpty(field.DisplayName))
                        {
                            // This is where we will eventually add code that uses an expression to plug in the actual
                            // value for a field name when curly braces are detected (or something).
                            content.AppendFormat(@"<Grid Grid.Row=""{0}""><TextBlock Grid.Column=""0"" Text=""{1}"" 
                                                Margin=""5,2"" Opacity="".6"" VerticalAlignment=""Center"" 
                                                TextWrapping=""Wrap"" /></Grid>",
                                numRows - 1, PopupInfo.SafeXML(field.DisplayName));
                        }

                        // Process this field regardless of value. We need to create a control that has binding so that as
                        // the value changes during editing the control will adapt and display null and not-null values
                        // properly.
                        object o = GetFieldValue(popupItem, field.Name);
                        StringBuilder element = GetFieldValueElement(field, o, numRows - 1);
                        if (element != null)
                        {
                            content.Append(element);
                        }

                        // Column separator
                        content.AppendFormat(@"<Rectangle Fill=""{{StaticResource BackgroundTextColorBrush}}""
                                                HorizontalAlignment=""Stretch"" VerticalAlignment=""Stretch""
                                                Grid.Column=""1"" Grid.Row=""{0}"" Opacity=""0.3"" Width=""1""/>", 
                                                numRows - 1);
                    }

                    #region Add Open Item hyperlink
                    if (layer is CustomGraphicsLayer)
                    {
                        ESRI.ArcGIS.Client.Application.Layout.Converters.LocalizationConverter converter = new Client.Application.Layout.Converters.LocalizationConverter();
                        gridRowDefinitions += "<RowDefinition Height=\"Auto\" />";
                        numRows++;
                        content.AppendFormat("<HyperlinkButton Content=\"{0}\" Grid.ColumnSpan=\"2\" Grid.Row=\"{1}\" CommandParameter=\"{{Binding}}\"><HyperlinkButton.Command><local:OpenItemCommand/></HyperlinkButton.Command></HyperlinkButton>",
                            converter.Get("OpenItem"), numRows - 1);
                    }
                    #endregion

                    if (!string.IsNullOrEmpty(gridRowDefinitions))
                        sb.AppendFormat("<Grid.RowDefinitions>{0}</Grid.RowDefinitions>", gridRowDefinitions);
                    sb.Append(content.ToString());
                }
                sb.Append("</Grid></DataTemplate>");
            }
			try
			{
				dt = (DataTemplate)System.Windows.Markup.XamlReader.Load(sb.ToString());
            }
			catch { /* No content */ }

			return dt;
		}
        const string attributeBindingRegex = @"({)([^}]*)(})";	// Regular expression to identify attribute bindings

        public static string ConvertExpressionWithFieldNames(PopupItem item, string formatter)
        {
            if (!string.IsNullOrEmpty(formatter))
            {
                StringBuilder sb = new StringBuilder();
                string[] splitStringArray = Regex.Split(formatter, attributeBindingRegex);
                bool isAttributeName = false;
                foreach (string str in splitStringArray)
                {
                    if (str == "{") { isAttributeName = true; continue; }
                    if (str == "}") { isAttributeName = false; continue; }
                    if (isAttributeName && item.Graphic.Attributes.ContainsKey(str))
                    {
                        var temp = MapTipsHelper.GetFieldValue(item, str);
                        sb.AppendFormat("{0}", temp);
                    }
                    else if (!isAttributeName)
                        sb.AppendFormat("{0}", str);
                }
                return sb.ToString().Replace("$LINEBREAK$", "<br/>").Replace("&amp;", "&").Replace("&lt;", "<").Replace("&gt;", ">").Replace("&apos;", "'").Replace("&quot;", "\"");
            }
            return null;
        }
        public static void GetTitle(PopupItem popupItem, LayerInformation info)
        {
            if (popupItem == null)
                return;

            string titleExpression = string.Empty;
            string title = string.Empty;
            
            Layer layer = popupItem.Layer;

            #region Get from developer override/webmap override
            //First get from developer override
            IDictionary<int, string> titleExpressions = ESRI.ArcGIS.Client.Extensibility.LayerProperties.GetPopupTitleExpressions(layer);
            //then if defined in web map
            if (titleExpressions == null && ESRI.ArcGIS.Mapping.Core.LayerExtensions.GetUsePopupFromWebMap(layer))
                titleExpressions = ESRI.ArcGIS.Mapping.Core.LayerExtensions.GetWebMapPopupTitleExpressions(layer);
            if (titleExpressions != null)
            {
                if (layer is GraphicsLayer)
                    titleExpression = (titleExpressions.ContainsKey(-1)) ? titleExpressions[-1] : null;
                else
                    titleExpression = (titleExpressions.ContainsKey(popupItem.LayerId)) ? titleExpressions[popupItem.LayerId] : null;
            }
            #endregion

            #region Get title from expression
            if (!string.IsNullOrWhiteSpace(titleExpression))
                title = ConvertExpressionWithFieldNames(popupItem, titleExpression);
            #endregion

            if (string.IsNullOrWhiteSpace(title))
            {
                if (layer is GraphicsLayer) //get from display field set in layer configuration
                {
                    #region Get from display field - Graphics layer
                    string displayFieldName = ESRI.ArcGIS.Mapping.Core.LayerExtensions.GetDisplayField(layer as GraphicsLayer);
                    if (!string.IsNullOrWhiteSpace(displayFieldName))
                    {
                        object o = GetFieldValue(popupItem, displayFieldName);
                        title = (o != null) ? o.ToString() : string.Empty;
                        if (string.IsNullOrWhiteSpace(popupItem.TitleExpression))
                            titleExpression = "{" + displayFieldName + "}";
                    }
                    #endregion
                }
                else if (!ESRI.ArcGIS.Mapping.Core.LayerExtensions.GetUsePopupFromWebMap(layer))
                {
                    #region Get from display field - dynamic layer
                    if (!string.IsNullOrEmpty(info.DisplayField))//get from display field set in layer configuration
                    {
                        object o = GetFieldValue(popupItem, info.DisplayField);
                        title = (o != null) ? o.ToString() : string.Empty;
                        titleExpression = "{" + info.DisplayField + "}";
                    }
                    #endregion
                }
            }

            if (title != null)
                title = title.Trim();
            popupItem.Title = title;
            popupItem.TitleExpression = titleExpression;
        }
        public static PopupItem GetPopupItem(Graphic graphic, Layer layer, IEnumerable<FieldInfo> fields, LayerInformation layerInfo, string layerName, string title = null, int? layerId = null)
        {
            if (layerInfo != null || layer is GraphicsLayer)
            {
                PopupItem popupItem = new PopupItem()
                {
                    Graphic = graphic,
                    Layer = layer,
                    Title = title,
                    LayerName = layerName,
                };
                if (layerId.HasValue) popupItem.LayerId = layerId.Value;

                if (layerInfo != null) GetMappedAttributes(graphic.Attributes, layerInfo);
                
                popupItem.FieldInfos = MapTipsHelper.ToFieldSettings(fields);
                MapTipsHelper.GetTitle(popupItem, layerInfo);

                bool hasContent = true;
                DataTemplate dt = MapTipsHelper.BuildMapTipDataTemplate(popupItem, out hasContent, layerInfo);
                if (!hasContent)
                    popupItem.DataTemplate = null;
                else if (dt != null)
                    popupItem.DataTemplate = dt;
                
                if (hasContent || (!string.IsNullOrWhiteSpace(popupItem.Title)))
                    return popupItem;

            }
            return null;
        }