CrmGridViewControl

Now we will take all of the controls we have built in this chapter and use them to create an editable grid control. We’ll use the CrmGridViewControl to quickly update records in Microsoft Dynamics CRM from an external Web page.

Programming the CrmGridViewControl

Adding theCrmGridViewControl class

  1. Right-click the ProgrammingWithDynamicsCrm4.Controls Project in Solution Explorer. Under Add, click New Item.

  2. In the Visual C# Items category, select the Class template.

  3. Type CrmGridViewControl.cs in the Name box and click Add.

The CrmGridViewControl inherits from the ASP.NET GridView control. As you can see from Example 14-6, we use a great deal of the inherited functionality.

Note

Note

The code in Example 14-6 uses a few static variables for caching metadata. The performance of this control can be greatly increased by using a full metadata cache in your application. See Chapter 8, for more details on how to build a metadata cache.

Example 14-6. CrmGridViewControl source code

using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using Microsoft.Crm.SdkTypeProxy;
using Microsoft.Crm.Sdk.Query;
using System.ComponentModel;
using System.Web.UI.WebControls;
using Microsoft.Crm.SdkTypeProxy.Metadata;
using System.Web.UI;
using Microsoft.Crm.Sdk.Metadata;
using System.Data;
using Microsoft.Crm.Sdk;
using System.Configuration;
using ProgrammingWithDynamicsCrm4.Utilities;

namespace ProgrammingWithDynamicsCrm4.Controls
{
    public class CrmGridViewControl : GridView
    {
        private static IDictionary<String, AttributeMetadata> _attributeCache =
    new Dictionary<String, AttributeMetadata>();
        private static object _attributeCacheLock = new object();
        private static IDictionary<String, EntityMetadata> _entityCache =
    new Dictionary<String, EntityMetadata>();
        private static object _entityCacheLock = new object();
        private MetadataService _metadataService =
    CrmServiceUtility.GetMetadataService(
        ConfigurationManager.AppSettings["CrmServer"],
        ConfigurationManager.AppSettings["OrgName"]);
        private CrmService _crmService =
    CrmServiceUtility.GetCrmService(ConfigurationManager.AppSettings["CrmServer"],
        ConfigurationManager.AppSettings["OrgName"]);

        private string _entityName;
        private string _fetchXml;

        public Guid? ViewId
        {
            get
            {
                object viewId = ViewState["ViewId"];

                if (viewId != null)
                    return (Guid)ViewState["ViewId"];
                else
                    return null;
            }
            set
            {
                 ViewState["ViewId"] = value;
            }
         }

         public bool IsUserQuery
         {
             get
             {
                 object isUserQuery = ViewState["IsUserQuery"];

                 if (isUserQuery != null)
                     return (bool)ViewState["IsUserQuery"];
                 else
                     return false;
             }
             set
             {
                 ViewState["IsUserQuery"] = value;
             }
         }

         [Browsable(false)]
         [EditorBrowsable(EditorBrowsableState.Never)]
         public override object DataSource
         {
             get
             {
                 return base.DataSource;
             }
             set
             {
                 base.DataSource = value;
             }
         }

         [Browsable(false)]
         [EditorBrowsable(EditorBrowsableState.Never)]
         public override string DataSourceID
         {
             get
             {
                 return base.DataSourceID;
             }
             set
             {
                 base.DataSourceID = value;
             }
         }

         [Browsable(false)]
         [EditorBrowsable(EditorBrowsableState.Never)]
         public override bool AutoGenerateColumns
         {
             get
             {
                 return base.AutoGenerateColumns;
             }
             set
             {
                 base.AutoGenerateColumns = value;
             }
         }

         [Browsable(false)]
         [EditorBrowsable(EditorBrowsableState.Never)]
         public override DataControlFieldCollection Columns
         {
             get
             {
                 if (base.Columns.Count == 0)
                     this.AddColumns();

                 return base.Columns;
             }
         }

         private void AddColumns()
         {
             if (string.IsNullOrEmpty(_fetchXml))
                 GetFetchXmlFromView();

             XmlDocument fetchDoc = new XmlDocument();
             fetchDoc.LoadXml(_fetchXml);

             XmlNode entityNode =
     fetchDoc.DocumentElement.SelectSingleNode("entity");

             _entityName = entityNode.Attributes["name"].Value;

             XmlNodeList attributes = entityNode.SelectNodes("attribute");

             string[] attributeNames = new string[attributes.Count];

             for (int i = 0; i < attributes.Count; i++)
             {
                 attributeNames[i] = attributes[i].Attributes["name"].Value;
             }

             // add the edit/update column
             CommandField editColumn = new CommandField();
             editColumn.ButtonType = ButtonType.Link;
             editColumn.CausesValidation = false;
             editColumn.ShowEditButton = true;
             editColumn.ShowCancelButton = true;
             editColumn.EditText = "Edit";
             editColumn.UpdateText = "Update";
             editColumn.CancelText = "Cancel";

             base.Columns.Add(editColumn);

             foreach( string attributeName in attributeNames )
             {
                 AttributeMetadata attributeMetadata =
     GetAttributeMetadata(_entityName, attributeName);

                 TemplateField templateField = new TemplateField();
                 templateField.HeaderText =
     attributeMetadata.DisplayName.UserLocLabel.Label;

                 if (attributeMetadata.AttributeType.Value ==
     AttributeType.PrimaryKey)
                     templateField.Visible = false;

                 templateField.ItemTemplate =
     new CrmGridViewTemplate(ListItemType.Item, _entityName, attributeName,
     attributeMetadata.AttributeType.Value, attributeMetadata.ValidForUpdate.Value);

                 CrmGridViewTemplate editTemplate =
     new CrmGridViewTemplate(ListItemType.EditItem, _entityName, attributeName,
     attributeMetadata.AttributeType.Value, attributeMetadata.ValidForUpdate.Value);

                 switch (attributeMetadata.AttributeType.Value)
                 {
                     case AttributeType.Lookup:
                     {
                         LookupAttributeMetadata lookupAttribute =
     (LookupAttributeMetadata)attributeMetadata;
                     string lookupEntity = lookupAttribute.Targets[0].ToString();

                     EntityMetadata lookupEntityMetadata =
     GetEntityMetadata(lookupEntity, EntityItems.EntityOnly);

                     editTemplate.EntityPicklistEntityName =
     lookupEntityMetadata.LogicalName;
                     editTemplate.EntityPicklistTextField =
     lookupEntityMetadata.PrimaryField;
                     editTemplate.EntityPicklistValueField =
     lookupEntityMetadata.PrimaryKey;
                    }
                    break;
                 }

                 templateField.EditItemTemplate = editTemplate;

                 base.Columns.Add(templateField);
             }
         }

         private void GetFetchXmlFromView()
         {
         if (!this.ViewId.HasValue)
             throw new Exception(
 String.Format("{0}: Please provide a value for ViewId.", this.ID));

         ColumnSet cols = new ColumnSet();
         cols.AddColumn("fetchxml");

         if (this.IsUserQuery)
         {
             userquery userQuery =
 (userquery)_crmService.Retrieve(EntityName.userquery.ToString(), ViewId.Value,
      cols);
             _fetchXml = userQuery.fetchxml;
         }
         else
         {
              savedquery savedQuery =
 (savedquery)_crmService.Retrieve(EntityName.savedquery.ToString(), ViewId.Value,
      cols);
              _fetchXml = savedQuery.fetchxml;
         }
      }

      private void CreateDataSource()
      {
          if (string.IsNullOrEmpty(_fetchXml))
              this.GetFetchXmlFromView();

          XmlDocument fetchDoc = new XmlDocument();
          fetchDoc.LoadXml(_fetchXml);

          XmlNode entityNode =
 fetchDoc.DocumentElement.SelectSingleNode("entity");

          _entityName = entityNode.Attributes["name"].Value;

          XmlNodeList attributes = entityNode.SelectNodes("attribute");

          string[] attributeNames = new string[attributes.Count];

          for (int i = 0; i < attributes.Count; i++)
          {
              attributeNames[i] = attributes[i].Attributes["name"].Value;
          }

          DataTable dt = new DataTable();

          // add columns to the new data table
          for (int i = 0; i < attributeNames.Length; i++)
          {
               dt.Columns.Add(attributeNames[i]);

               // check the metadata for each attribute
               // we will want to store the name attribute as well as the value for
               // certain attributes
               AttributeMetadata attributeMetadata =
   this.GetAttributeMetadata(_entityName, attributeNames[i]);

               switch (attributeMetadata.AttributeType.Value)
               {
                   case AttributeType.Boolean:
                   case AttributeType.Lookup:
                   case AttributeType.Picklist:
                   case AttributeType.Status:
                       {
                           dt.Columns.Add(String.Format("{0}name",
    attributeNames[i]));
                       }
                       break;
               }
           }

           string fetchResults = _crmService.Fetch(_fetchXml);

           XmlDocument resultsDoc = new XmlDocument();
           resultsDoc.LoadXml(fetchResults);

           XmlNodeList results = resultsDoc.DocumentElement.SelectNodes("result");

           foreach (XmlNode result in results)
           {
               DataRow dr = dt.NewRow();

               foreach( DataColumn column in dt.Columns )
               {
                   string columnName = column.ColumnName;

                   XmlNode columnNode = result.SelectSingleNode(columnName);

                   if (columnNode != null)
                   {
                       AttributeMetadata resultAttributeMetadata =
   this.GetAttributeMetadata(_entityName, columnName);

                       switch (resultAttributeMetadata.AttributeType.Value)
                       {
                           case AttributeType.Boolean:
                           case AttributeType.Lookup:
                           case AttributeType.Picklist:
                           case AttributeType.Status:
                               {
                                   dr[columnName] = columnNode.InnerText;
                                   dr[columnName + "name"] =
   columnNode.Attributes["name"].Value;
                               }
                               break;
                           case AttributeType.DateTime:
                               {
                                   dr[columnName] = columnNode.InnerText;
                               }
                               break;
                           default:
                               {
                                   dr[columnName] = columnNode.InnerText;
                               }
                               break;
                       }
                   }
               }

               dt.Rows.Add(dr);
           }

           this.DataSource = dt;
       }


       private AttributeMetadata GetAttributeMetadata(string entityName,
   string attributeName)
       {
           string key = GetAttributeKey(entityName, attributeName);
           AttributeMetadata attributeMetadata = null;

           if (!_attributeCache.TryGetValue(key, out attributeMetadata))
           {
               lock (_attributeCacheLock)
               {
                   if (!_attributeCache.TryGetValue(key, out attributeMetadata))
                   {
                       RetrieveAttributeRequest attributeRequest =
   new RetrieveAttributeRequest();
                       attributeRequest.EntityLogicalName = entityName;
                       attributeRequest.LogicalName = attributeName;
                       RetrieveAttributeResponse attributeResponse =
   (RetrieveAttributeResponse)_metadataService.Execute(attributeRequest);
                       attributeMetadata = attributeResponse.AttributeMetadata;

                        _attributeCache.Add(key, attributeMetadata);
                   }
               }
           }
           return attributeMetadata;
       }

       private string GetAttributeKey(string entityName, string attributeName)
       {
           return String.Format("{0}|{1}", entityName, attributeName);
       }

       private EntityMetadata GetEntityMetadata(string entityName,
   EntityItems entityItems)
       {
           string key = GetEntityKey(entityName, entityItems);
           EntityMetadata entityMetadata = null;

           if (!_entityCache.TryGetValue(key, out entityMetadata))
           {
               lock (_entityCacheLock)
               {
                   if (!_entityCache.TryGetValue(key, out entityMetadata))
                   {
                       RetrieveEntityRequest entityRequest =
   new RetrieveEntityRequest();
                       entityRequest.LogicalName = entityName;
                       entityRequest.EntityItems = entityItems;
                       entityRequest.RetrieveAsIfPublished = false;

                       RetrieveEntityResponse entityResponse =
    (RetrieveEntityResponse)_metadataService.Execute(entityRequest);

                       entityMetadata = entityResponse.EntityMetadata;

                       _entityCache.Add(key, entityMetadata);
                    }
                }
            }
            return entityMetadata;
       }

       private string GetEntityKey(string entityName, EntityItems entityItems)
       {
           return String.Format("{0}|{1}", entityName, entityItems.ToString());
       }

       protected override void OnLoad(EventArgs e)
       {
           if (!Page.IsPostBack)
           {
               this.AutoGenerateColumns = false;
               this.AddColumns();
               this.CreateDataSource();
               this.DataBind();
           }

           base.OnLoad(e);
       }

       protected override void OnRowEditing(GridViewEditEventArgs e)
       {
           this.EditIndex = e.NewEditIndex;
           this.CreateDataSource();
           this.DataBind();
       }

       protected override void OnRowUpdating(GridViewUpdateEventArgs e)
       {
           DynamicEntity updateEntity = new DynamicEntity();
           updateEntity.Name = _entityName;

           GridViewRow row = this.Rows[e.RowIndex];

           foreach( TableCell cell in row.Cells )
           {
               if (cell.Controls.Count > 0)
               {
                   Control control = cell.Controls[0];

                   if (control is Label)
                   {
                       Label label = (Label)control;

                       if (label.ID == String.Format("{0}id", _entityName))
                       {
                           KeyProperty key = new KeyProperty();
                           key.Name = label.ID;
                           key.Value = new Key();
                           key.Value.Value = new Guid(label.Text);

                           updateEntity.Properties.Add(key);
                       }

                       continue;
                   }

                   if (control is TextBox)
                   {
                       TextBox textBox = (TextBox)control;

                       StringProperty stringProp = new StringProperty();
                       stringProp.Name = textBox.ID;
                       stringProp.Value = textBox.Text;

                       updateEntity.Properties.Add(stringProp);

                       continue;
                   }

                   if (control is CrmBooleanControl)
                   {
                       CrmBooleanControl crmBoolean = (CrmBooleanControl)control;

                       CrmBooleanProperty booleanProp = new CrmBooleanProperty();
                       booleanProp.Name = crmBoolean.ID;
                       booleanProp.Value = new CrmBoolean();

                       bool value = false;
                       if (crmBoolean.SelectedValue == "1")
                           value = true;

                       booleanProp.Value.Value = value;

                       updateEntity.Properties.Add(booleanProp);

                       continue;

                   }

                   if (control is CrmDateTimeControl)
                   {
                       CrmDateTimeControl crmDateTime =
   (CrmDateTimeControl)control;

                       if (crmDateTime.Value != DateTime.MinValue)
                       {
                           CrmDateTimeProperty dateTimeProp =
   new CrmDateTimeProperty();
                           dateTimeProp.Name = crmDateTime.ID;
                           dateTimeProp.Value = new CrmDateTime();
                           dateTimeProp.Value.Value = crmDateTime.Value.ToString();

                           updateEntity.Properties.Add(dateTimeProp);
                       }

                       continue;
                   }

                   if (control is CrmEntityPicklistControl)
                   {
                       CrmEntityPicklistControl crmEntityPicklist =
   (CrmEntityPicklistControl)control;

                       LookupProperty lookupProp = new LookupProperty();
                       lookupProp.Name = crmEntityPicklist.ID;

                       Lookup lookup = new Lookup();
                       lookup.name = crmEntityPicklist.SelectedItem.Text;
                       lookup.Value = new Guid(crmEntityPicklist.SelectedValue);

                       lookupProp.Value = lookup;

                       updateEntity.Properties.Add(lookupProp);

                       continue;
                   }

                   if (control is CrmPicklistControl)
                   {
                       CrmPicklistControl crmPicklist =
   (CrmPicklistControl)control;

                       string attributeName = crmPicklist.ID;
                       AttributeMetadata attributeMetadata =
   this.GetAttributeMetadata(_entityName, attributeName);

                       if (!string.IsNullOrEmpty(crmPicklist.SelectedValue))
                       {
                           switch (attributeMetadata.AttributeType.Value)
                           {
                               case AttributeType.Picklist:
                                   {

                                       PicklistProperty picklist =
   new PicklistProperty();
                                       picklist.Name = attributeName;
                                       picklist.Value = new Picklist();
                                       picklist.Value.Value =
   int.Parse(crmPicklist.SelectedValue);

                                       updateEntity.Properties.Add(picklist);
                                   }
                                   break;
                               case AttributeType.Status:
                                   {
                                       StatusProperty status =
   new StatusProperty();
                                       status.Name = attributeName;
                                       status.Value = new Status();
                                       status.Value.Value =
   int.Parse(crmPicklist.SelectedValue);

                                       updateEntity.Properties.Add(status);
                                   }
                                   break;
                           }
                       }

                       continue;
                   }
               }
           }

           TargetUpdateDynamic targetUpdateDynamic = new TargetUpdateDynamic();
           targetUpdateDynamic.Entity = updateEntity;

           UpdateRequest updateRequest = new UpdateRequest();
           updateRequest.Target = targetUpdateDynamic;

           UpdateResponse updateResponse =
    (UpdateResponse)_crmService.Execute(updateRequest);

           this.EditIndex = -1;
           this.CreateDataSource();
           this.DataBind();
       }


       protected override void OnRowCancelingEdit(GridViewCancelEditEventArgs e)
       {
           this.EditIndex = -1;
           this.CreateDataSource();
           this.DataBind();
       }
   }
}

CrmGridViewControl Properties

Table 14-5 lists the properties added to the CrmGridViewControl. We’ll populate the grid using a saved query from Microsoft Dynamics CRM. This can either be a system view or a saved Advanced Find. The unique identifier (Guid) of this record is set in the ViewId property and then the grid uses the value set in the IsUserQuery property to determine which type of record it is.

Table 14-5. CrmGridViewControl Properties

Property Name

Type

Description

ViewId

Guid

Unique identifier of the system view or advanced find view used to populate the grid

IsUserQuery

bool

Determines whether the view being used is a system view or a saved advanced find

Because we are adding the columns and setting up the data source based on the ViewId, we need to override and hide the following properties from the Visual Studio designer and C# code editor’s IntelliSense:

  • DataSource

  • DataSourceID

  • AutoGenerateColumns

  • Columns

Adding the Columns

The CrmGridViewControl overrides the Columns collection inherited from the System.Web. UI.GridView class. In the "get" of this property, if the base class’s column collection is empty, we call our AddColumns method.

Before we can actually add the columns, we need to retrieve the Fetch XML query that drives the view specified in the ViewId property. From the retrieved Fetch XML, we can determine the entity and the list of attributes that will be returned for that entity.

The first column we add is for the command buttons. This column adds the Edit, Update, and Cancel buttons to the first column of our grid.

// add the edit/update column
CommandField editColumn = new CommandField();
editColumn.ButtonType = ButtonType.Link;
editColumn.CausesValidation = false;
editColumn.ShowEditButton = true;
editColumn.ShowCancelButton = true;
editColumn.EditText = "Edit";
editColumn.UpdateText = "Update";
editColumn.CancelText = "Cancel";

Next we loop through the attributes and add a column for each. Inside the loop we retrieve the AttributeMetadata for the current attribute. We will be making many calls to the retrieve the metadata, so we create a method that stores the AttributeMetadata in a Dictionary. The GetAttributeMetadata method checks the Dictionary to see whether we have already retrieved metadata for that attribute, and either returns the stored metadata or makes a call the MetadataService.

Warning

Warning

Since the _attributeCache Dictionary is static, it will need to be refreshed if any changes to the metadata occur.

private AttributeMetadata GetAttributeMetadata(string entityName, string attributeName)
{
    string key = GetAttributeKey(entityName, attributeName);
    AttributeMetadata attributeMetadata = null;

    if (!_attributeCache.TryGetValue(key, out attributeMetadata))
    {
        lock (_attributeCacheLock)
        {
            if (!_attributeCache.TryGetValue(key, out attributeMetadata))
            {
                RetrieveAttributeRequest attributeRequest = new RetrieveAttributeRequest();
                attributeRequest.EntityLogicalName = entityName;
                attributeRequest.LogicalName = attributeName;
                RetrieveAttributeResponse attributeResponse =
     (RetrieveAttributeResponse)_metadataService.Execute(attributeRequest);
                attributeMetadata = attributeResponse.AttributeMetadata;

                _attributeCache.Add(key, attributeMetadata);
            }
        }
    }
    return attributeMetadata;
}

After we have the AttributeMetadata, we first check to see whether it is the entity’s primary key. To do this we simply check to see whether the AttributeType equals PrimaryKey. If it does, we want to add it as a hidden column. We’ll use this later when we are updating the record in Microsoft Dynamics CRM. The columns will all be added as ASP.NET TemplateFields. Because we are going to have some pretty complex logic in our columns, we need to create our own template.

Creating the CrmGridViewTemplate

Adding theCrmGridViewTemplate class

  1. Right-click the ProgrammingWithDynamicsCrm4.Controls Project in Solution Explorer. Under Add, click New Item.

  2. In the Visual C# Items category, select the Class template.

  3. Type CrmGridViewTemplate.cs in the Name box and click Add.

The CrmGridViewControl has two modes, Read Only and Edit. The CrmGridViewTemplate controls what gets rendered for each grid mode. For the Read Only display, we want to render an ASP.NET Label control that displays the value, and in Edit mode we want to render a control that allows the end user to update the values in that row. We will render a different edit control based on the AttributeType of the current column’s AttributeMetadata. Example 14-7 has the source code for the CrmGridViewTemplate. This class implements the ITemplate interface, which forces us to add the InstantiateIn method.

Example 14-7. CrmGridViewTemplate source code

using System;
using System.Collections.Generic;
using System.Text;
using System.Web.UI;
using System.Web.UI.WebControls;
using Microsoft.Crm.Sdk.Metadata;

namespace ProgrammingWithDynamicsCrm4.Controls
{
    public class CrmGridViewTemplate : ITemplate
    {
        private ListItemType _itemType;
        string _entityName;
        private string _fieldName;
        private AttributeType _attributeType;
        private bool _validForUpdate;

        public string EntityPicklistEntityName { get; set; }
        public string EntityPicklistTextField { get; set; }
        public string EntityPicklistValueField { get; set; }

        public CrmGridViewTemplate(ListItemType itemType,
                                  string entityName,
                                  string fieldName,
                                  AttributeType attributeType,
                                  bool validForUpdate)
        {
            _itemType = itemType;
            _entityName = entityName;
            _fieldName = fieldName;
            _attributeType = attributeType;
            _validForUpdate = validForUpdate;
        }

        public void InstantiateIn(Control container)
        {
            switch (_itemType)
            {
                case ListItemType.Item:
                    {
                        Label label = new Label();
                        label.ID = _fieldName;
                        label.DataBinding +=
                                             new EventHandler(Control_DataBinding);
                        container.Controls.Add(label);
                    }
                    break;
                case ListItemType.EditItem:
                    {
                        if (_validForUpdate &&
                                        _attributeType != AttributeType.PrimaryKey)
                        {

                            switch (_attributeType)
                            {
                                case AttributeType.Boolean:
                                    {
                                        CrmBooleanControl crmBoolean =
                                                            new CrmBooleanControl();
                                        crmBoolean.ID = _fieldName;
                                        crmBoolean.EntityName = _entityName;
                                        crmBoolean.AttributeName = _fieldName;
                                        crmBoolean.DataBinding +=
                                              new EventHandler(Control_DataBinding);
                                        container.Controls.Add(crmBoolean);
                                    }
                                    break;
                                case AttributeType.DateTime:
                                    {
                                        CrmDateTimeControl crmDateTime =
                                                           new CrmDateTimeControl();
                                        crmDateTime.ID = _fieldName;
                                        crmDateTime.EntityName = _entityName;
                                        crmDateTime.AttributeName = _fieldName;
                                        crmDateTime.DataBinding +=
                                              new EventHandler(Control_DataBinding);
                                         container.Controls.Add(crmDateTime);
                                     }
                                     break;
                                 case AttributeType.Lookup:
                                     {
                                         CrmEntityPicklistControl crmEntityPicklist =
                                                      new CrmEntityPicklistControl();
                                         crmEntityPicklist.ID = _fieldName;
                                         crmEntityPicklist.EntityName =
                                                       this.EntityPicklistEntityName;
                                         crmEntityPicklist.DataValueField =
                                                       this.EntityPicklistValueField;
                                         crmEntityPicklist.DataTextField =
                                                        this.EntityPicklistTextField;
                                         crmEntityPicklist.DataBinding +=
                                               new EventHandler(Control_DataBinding);
                                         container.Controls.Add(crmEntityPicklist);
                                     }
                                     break;
                                 case AttributeType.Picklist:
                                 case AttributeType.Status:
                                     {
                                         CrmPicklistControl crmPicklist =
                                                            new CrmPicklistControl();
                                         crmPicklist.ID = _fieldName;
                                         crmPicklist.EntityName = _entityName;
                                         crmPicklist.AttributeName = _fieldName;
                                         crmPicklist.DataBinding +=
                                               new EventHandler(Control_DataBinding);
                                         container.Controls.Add(crmPicklist);
                                     }
                                     break;
                                 default:
                                     {
                                         TextBox textBox = new TextBox();
                                         textBox.ID = _fieldName;
                                         textBox.DataBinding +=
                                               new EventHandler(Control_DataBinding);
                                         container.Controls.Add(textBox);
                                     }
                                     break;
                             }
                        }
                        else
                        {
                             Label label = new Label();
                             label.ID = _fieldName;
                             label.DataBinding +=
                                               new EventHandler(Control_DataBinding);
                             container.Controls.Add(label);
                        }

                    }
                    break;
             }
         }

         private void Control_DataBinding(object sender, EventArgs e)
         {
             switch (_itemType)
             {
                 case ListItemType.Item:
                     {
                         Label label = (Label)sender;
                         GridViewRow container = (GridViewRow)label.NamingContainer;
                         object value = null;
                         switch (_attributeType)
                         {
                             case AttributeType.Boolean:
                             case AttributeType.Lookup:
                             case AttributeType.Picklist:
                             case AttributeType.Status:
                                 {
                                     value = DataBinder.Eval(container.DataItem,
                                                            String.Format("{0}name",
                                                            _fieldName));
                                 }
                                 break;
                             default:
                                 {
                                     value = DataBinder.Eval(container.DataItem,
                                                            _fieldName);
                                 }
                                 break;
                         }

                         if (value != null && value.GetType() != typeof(DBNull))
                         {
                             label.Text = (string)value;
                         }
                     }
                    break;
                case ListItemType.EditItem:
                    {
                        WebControl editControl = (WebControl)sender;
                        GridViewRow container =
                                          (GridViewRow)editControl.NamingContainer;

                        object value = DataBinder.Eval(container.DataItem,
                                                      _fieldName);

                        if (value.GetType() != typeof(DBNull))
                        {
                            if (_validForUpdate)
                            {
                                switch (_attributeType)
                                {
                                    case AttributeType.Boolean:
                                        {
                                            CrmBooleanControl crmBoolean =
                                                     (CrmBooleanControl)editControl;
                                            crmBoolean.SelectedValue =
                                                                      (string)value;
                                        }
                                        break;
                                    case AttributeType.DateTime:
                                        {
                                            CrmDateTimeControl crmDateTime =
                                                   (CrmDateTimeControl)editControl;
                                            crmDateTime.Value =
                                                         Convert.ToDateTime(value);
                                         }
                                         break;
                                     case AttributeType.Lookup:
                                         {
                                             CrmEntityPicklistControl
                                                             crmEntityPicklist =
                                               (CrmEntityPicklistControl)editControl;
                                             crmEntityPicklist.SelectedValue =
                                                                       (string)value;
                                         }
                                         break;
                                     case AttributeType.Picklist:
                                     case AttributeType.Status:
                                         {
                                             CrmPicklistControl crmPicklist =
                                                     (CrmPicklistControl)editControl;
                                             crmPicklist.SelectedValue =
                                                                       (string)value;
                                         }
                                         break;
                                     default:
                                         {
                                             TextBox textBox = (TextBox)editControl;
                                             textBox.Text = (string)value;
                                         }
                                         break;
                                 }
                            }
                            else
                            {
                                 Label label = (Label)editControl;

                                 switch (_attributeType)
                                 {
                                     case AttributeType.Boolean:
                                     case AttributeType.Lookup:
                                     case AttributeType.Picklist:
                                     case AttributeType.Status:
                                         {
                                             value =
                                                 DataBinder.Eval(container.DataItem,
                                                             String.Format("{0}name",
                                                             _fieldName));
                                         }
                                         break;
                                 }

                                 if (value.GetType() != typeof(DBNull))
                                     label.Text = (string)value;
                            }
                        }
                        break;
                     }
             }
         }
     }
}

Each Template field takes two templates, one for the item template and one for the edit template. The values passed into the CrmGridViewTemplate constructor allow us to determine which type of template is being created. Table 14-6 lists the members that are set based on the arguments from the constructor.

Table 14-6. CrmGridViewTemplate Members

Member Name

Type

Description

_itemType

ListItemType

The type of item being set. Valid values are Edit and EditItem.

_entityName

string

The entity name to retrieve records from.

_fieldName

string

The schema name of the attribute used for the current column.

_attributeType

AttributeType

The AttributeType value from the current column’s AttributeMetadata. Use this to determine which type of control to render.

_validForUpdate

bool

Determines whether the column should render an edit control. This property’s value is based on the AttributeMetadata’s ValidForUpdate property.

Adding the Item template for each AttributeType will be accomplished in the same manner.

templateField.ItemTemplate = new CrmGridViewTemplate(ListItemType.Item,
                                   _entityName,
                                   attributeNames[i],
                                   attributeMetadata.AttributeType.Value,
                                   attributeMetadata.ValidForUpdate.Value);

Each attribute renders a Label containing the data for that column. Notice that we add a new EventHandler to the DataBinding event on the Label control. This allows us to add the correct value to the Label control’s Text property during data binding. For attributes with the types Picklist, Status, Boolean, and Lookup we want to display the text name for the value instead of the underlying int or Guid. We will talk more about how this is accomplished in the next section, "Creating the DataSource." If you look in the Control_DataBinding method of the CrmGridViewTemplate, you can see that we grab our value for these attribute types from a column named by concatenating the field name and the text "name".

The Edit Item logic is a little more complicated than the read-only items. Based on the AttributeType we render a different control. However, if the attribute is not valid for an update through the Microsoft Dynamics CRM SDK API, we render a read-only label. Table 14-7 lists the attribute types and which server control they render in the CrmGridViewControl control’s edit mode.

Table 14-7. CrmGridVeiwTemplate Control Mapping

AttributeType

Control

Boolean

CrmBooleanControl

DateTime

CrmDateTimeControl

Picklist

CrmPicklistControl

Status

CrmPicklistControl

String

TextBox

These controls are all created in the InstantiateIn method. The properties of the newly created controls are set to the values of the members of the CrmGridViewControl, and the Control_DataBinding method is added to the DataBinding event. The Lookup AttributeType is a special case. Because our CrmEntityPicklistControl is used to grab records for an entity other than the one being used to populate the grid, we need to add a few extra properties to the CrmGridViewTemplate class. These properties are listed in Table 14-8.

Table 14-8. CrmGridViewTemplate Properties

Property Name

Type

Description

EntityPicklistEntityName

string

Entity name to be set on the CrmEntityPicklistControl

EntityPicklistTextField

string

Field name to be set for the CrmEntityPicklistControl’sDataTextField

EntityPicklistValueField

string

Field name to be set for the CrmEntityPicklistControl’sDataValueField.

If you look back to the source code of the AddColumn method, you can see where these properties are being set for attributes of type Lookup:

case AttributeType.Lookup:
    {
        LookupAttributeMetadata lookupAttribute =
    (LookupAttributeMetadata)attributeMetadata;
        string lookupEntity = lookupAttribute.Targets[0].ToString();

        EntityMetadata lookupEntityMetadata = GetEntityMetadata(lookupEntity,
     EntityItems.EntityOnly);

        editTemplate.EntityPicklistEntityName = lookupEntityMetadata.LogicalName;
        editTemplate.EntityPicklistTextField = lookupEntityMetadata.PrimaryField;
        editTemplate.EntityPicklistValueField = lookupEntityMetadata.PrimaryKey;
    }
    break;

We use the LookupAttributeMetadata target as the EntityPicklistEntityName, and then we retrieve the EntityMetadata for that target entity. We can then use the entity’s primary field and primary key for the EntityPicklistTextField and EntityPicklistValueField, respectively.

Inside of the Control_DataBinding method, we grab the value from the DataSource and populate our control if need be. Now that the template is set, we can create a data source.

Creating the DataSource

In the CreateDataSource method (Example 14-6), we create a DataTable that we can bind our grid to. We add the columns for our DataTable by grabbing the attributes from the Fetch XML in the same way we did for the grid columns. Each attribute has a corresponding column in the DataTable. Attributes for the types Boolean, Picklist, Status, and Lookup have an extra column. Because these attribute types return an integer or Guid value, we need to add an extra column to store the text value to display to the user:

switch (attributeMetadata.AttributeType.Value)
                {
                    case AttributeType.Boolean:
                    case AttributeType.Lookup:
                    case AttributeType.Picklist:
                    case AttributeType.Status:
                        {
                            dt.Columns.Add(String.Format("{0}name", attributeNames[i]));
                        }
                        break;
                }

Next we call the CrmService Fetch method to retrieve our results. We can then use XPath to loop through our result set and populate the DataTable.

Updating the Record in Microsoft Dynamics CRM

To enable the row editing on the grid, we added an override for the OnRowEditing method. In this method, we set the edit index for the grid and then bind the data. The page then renders the grid populated with our edit controls along with an Update and Cancel button in place of the Edit button. Handling the Cancel button is easy: We add an override to the OnRowCancelingEdit method and set the edit index to -1. This renders our grid in read-only mode without making any changes to the underlying data. The OnRowUpdating method is added to handle when the end user clicks the Update button.

In the OnRowUpdating method we create a DynamicEntity and loop through each of our grid cells adding properties to the Properties collection on the DynamicEntity. While looping through the cells we will use reflection to figure out the type of control contained in the cell. After we have this, we can retrieve the updated values and add the property. After we’ve added all of the properties, we use the CrmService to update our entity record and then set the grid’s edit index back to -1.

Testing the CrmGridViewControl

Adding theCrmGridViewControl test page

  1. Open the ProgrammingWithDynamicsCrm4.Web project in Visual Studio.

  2. Right-click the project name in Solution Explorer, and click Add New Item.

  3. Select the Web Form template and type the name CrmGridViewControlPage.aspx in the Name box. Click OK.

To ensure that we test with each of the custom server controls we created in this chapter, we first create an Advanced Find in Microsoft Dynamics CRM. Create a new Advanced Find query that returns the following columns on the Lead entity:

  • Name

  • Source Campaign

  • Do Not Allow E-mail

  • Est. Close Date

  • Lead Source Code

Save the Advanced Find query. Now you can obtain the Guid of your saved query, which can be found in the UserQueryBase table of your Microsoft Dynamics CRM database.

Add <crm:CrmGridViewControl id="crmGrid" ViewId="2A1E0690-1B1D-DD11-8839-0019B9F8F548" IsUserQuery="true" runat="server" /> inside of the form tag of the CrmGridViewControlPage.aspx file:

<crm:CrmGridViewControl id="crmGrid"
                        ViewId="2A1E0690-1B1D-DD11-8839-0019B9F8F548"
                        IsUserQuery="true"
                        runat="server" />

Note

Note

Replace the ViewId value in the preceding code snippet with the Guid from your saved query.

You can now see a read-only grid of Lead records returned from the Advanced Find query (Figure 14-6).

CrmGridViewControl

Figure 14-6. CrmGridViewControl

Click the Edit button on one of the grid rows to turn on that row’s edit mode (Figure 14-7).

CrmGridViewControl in edit mode

Figure 14-7. CrmGridViewControl in edit mode

Now you can see all of our edit controls rendered for each of the different attribute types we returned in our query. Notice that the Name column is still read-only. The fullname attribute on the Lead is not valid for updates through the Microsoft Dynamics CRM SDK API. Update the values in the edit mode row and click Update (Figure 14-8).

CrmGridViewControl after editing

Figure 14-8. CrmGridViewControl after editing

Now the values in that record have been updated in Microsoft Dynamics CRM.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset