Preface

At a high level, the implementation of a Microsoft Dynamics CRM Portal allows for data from non-CRM users to interact with data from the Dynamics CRM System via a public website.

Please see our guide to portal development for Microsoft Dynamics CRM to learn more about the essentials of developing a portal solution for your Dynamics CRM Organization.

While the aforementioned blog post covers the necessary steps to get started, there’s a lot of additional functionality that can be added to your portal. This blog post will specifically address the implementation of a clickable GridView control to mimic the View functionality in Dynamics CRM.

Dynamics CRM Views

One of the core features of Dynamics CRM is the ability to use Views. Views show data in a tabular and filterable format, allowing for users to work with multiple records at one time. This crucial functionality can be replicated in a portal solution using the ASP.NET Web Forms GridView Control.

As a part of the portal controls made available by Microsoft, there exist two methods of implementing a GridView that allows for the display of filtered, tabular data against any entity in CRM. More details about implementing these controls can be found in the official Microsoft Dynamics CRM documentation:

Create a webpage – Contact Grid 1
Create another webpage – Contact Grid 2

Both GridViews are straightforward and can be quickly and easily merged into an .aspx page without having to modify anything in the back-end code. However, there are a couple of commonly asked features that require a bit of extra work:

For example, you might want to enable the portal users to view more fields than what is shown in the GridView. In addition, you might also want to enable your users to edit their portal information.

The pre-baked controls fall short in addressing either use case, which led me to consider another approach.

Scenario

Imagine that you belong to an organization that no longer wants to have its salespeople manually maintain and enter Account and Contact information in Dynamics CRM. Instead, you want your customers to enter Account and Contact information through the portal.

This new process will definitely reduce the workload on the salespeople by having your customers do the work themselves. It will also hold the customers accountable to keep their own account data, such as address information and phone number, up to date.

Your customers will vary in size and have varying numbers of relevant contacts.

Some Accounts may have just one point of contact, while others may have 5 or 10. This situation where a 1-N relationship makes sense is where we can leverage the aforementioned GridView solution to allow Accounts to add and edit Contact Information.

So, anywhere you want to duplicate the view functionality in CRM and make it clickable, this solution will work for you!

Solution

Like the previous post on portal development for Dynamics CRM, I will be working on a Visual Studio solution – in this scenario developed for Microsoft Dynamics CRM 2015.

Click here to download the Visual Studio solution used in this example.

For this demo, I will be authenticating against the Account entity instead of Contact because we want one Primary Account holder to log in and simply manage that Account’s contacts. So basically, I will have username and password fields on the Account form rather than the Contact form.

*I won’t be demonstrating how to encrypt passwords but if you’re actually implementing a portal solution for your Dynamics CRM deployment, you need to ensure that passwords are encrypted *

So, I’ll be starting by logged in as an Account with access to adding Contacts in a Gridview.

Feature 1: Add New Contacts

First, we want to enable a customer to “Add New Contact”.

To do this, we’ll need to create a Button which will redirect the logged-in Account to another page to start entering information. Be sure to pass along the Account ID so we can use it to set the parent customer field on the contact:

protected void AddNewContactButton_Click(object sender, EventArgs e)
{
Response.Redirect(“Contact.aspx?AccountID=” + accountID, false);
}

On this new Contact page we’ll allow the user to enter First Name, Last Name, Phone Number, and Contact Type:

<label>First Name:</label><asp:TextBox runat=”server”
ID=”FirstNameTextbox”></asp:TextBox><br />

<label>Last Name:</label><asp:TextBox runat=”server”
ID=”LastNameTextbox”></asp:TextBox><br />

<label>Phone Number:</label><asp:TextBox runat=”server”
ID=”PhoneNumberTextbox”></asp:TextBox><br />

<label>Contact Type:</label><asp:DropDownList runat=”server”
ID=”ContactTypeDDL”></asp:DropDownList><br />

We’ll also need to add a Save Button to commit the new changes to CRM and redirect back to the AccountGridview page:

protected void SaveButton_Click(object sender, EventArgs e)
{
try {
Entity newContact = new Entity(“contact”);
newContact[“firstname”] = FirstNameTextbox.Text;
newContact[“lastname”] = LastNameTextbox.Text;
newContact[“telephone1”] = PhoneNumberTextbox.Text;
newContact[“firstname”] = FirstNameTextbox.Text;

using (OrganizationService _service = new
OrganizationService(connection)) {

_service.Create(newContact);
}
}
catch {
throw;
}
Response.Redirect(“AccountGridview.aspx?AccountID=” + accountID);
}

Feature 2: Making a Clickable GridView

First add the GridView control to the markup:

<asp:GridView runat=”server” ID=”ContactsGrid”
OnRowDataBound=”ContactsGrid_RowDataBound”>
</asp:GridView>

Next, we’ll need to get the filter criteria for the GridView. In our scenario, we’ll get all active Contacts whose parent customer equals the Account, and whatever other fields we want to display on the GridView:

//Get all active contacts where parent customer is this account
string fetchContacts = @”<fetch version=’1.0′ output-format=’xml-platform’
mapping=’logical’ distinct=’false’>

<entity name=’contact’>
<attribute name=’fullname’ />
<attribute name=’telephone1′ />
<attribute name=’contactid’ />
<attribute name=’new_contacttype’ />
<order attribute=’fullname’ descending=’false’ />
<filter type=’and’>
<condition attribute=’statecode’ operator=’eq’ value=’0′ />
<condition attribute=’parentcustomerid’ operator=’eq’ value='” + accountID + “‘ />” +
@”</filter>
</entity>
</fetch>”;

Then, we’ll retrieve the Contacts based on the criteria and dynamically add the results to the GridView (you’ll also need to add a reference to System.Data for this):

using (OrganizationService _service = new OrganizationService(connection))
{
EntityCollection retContacts = _service.RetrieveMultiple(new FetchExpression(fetchContacts));
if (retContacts.Entities.Count > 0)
{
DataSet contactData = new DataSet();
DataTable dt = new DataTable();

dt.Columns.Add(“Full Name”);
dt.Columns.Add(“Telephone”);
dt.Columns.Add(“Contact Type”);
dt.Columns.Add(“ContactID”);

foreach (var contact in retContacts.Entities) {
DataRow dr = dt.NewRow();
dr[“Full Name”] = contact[“fullname”].ToString();
dr[“Telephone”] = contact[“telephone1”].ToString();
dr[“Contact Type”] = GetOptionSetName(“contact”, “new_contacttype”, ((OptionSetValue)contact[“new_contacttype”]).Value, _service);
dr[“ContactID”] = contact.Id;

dt.Rows.Add(dr);
}
contactData.Tables.Add(dt);
ContactsGrid.DataSource = contactData;
ContactsGrid.DataBind(); }
}

If you were paying close attention, you’ll notice that the Gridview Databind code includes a line to set ContactID for a data row.

This is because we’ll be using this field to make the Gridview clickable to navigate to the existing Contact so that changes can be made.

We’ll perform the necessary operations to enable click-ability in the DataRowBound event, which occurs every time a row gets data bound:

protected void ContactsGrid_RowDataBound(object sender, GridViewRowEventArgs e)
{
//Hide the Guid in the 3rd index
e.Row.Cells[3].Visible = false;

//use the value to create a new URL so that existing Contact ID can be passed to Contact.aspx
if (e.Row.RowType == DataControlRowType.DataRow)
{
// Get the value in the hyperlink column.
string HyperLinkValue = e.Row.Cells[0].Text;

HyperLink myLink = new HyperLink();
myLink.NavigateUrl = “Contact.aspx?AccountID=” + accountID + “&ContactID=” + e.Row.Cells[3].Text;
myLink.Text = HyperLinkValue;
// then add the control to the cell.
e.Row.Cells[0].Controls.Add(myLink);
}
}

Feature 3: Allowing Edit Functionality on Contact Page

Finally, we can extend our Contact page to allow for edit functionality.

First, we will be checking to see if there is a Query String parameter passed for ContactID, and if so, flag the Page as an edit. We will handle this in the Page_PreInit function and add some global variables that will be used in other page methods:

bool contactExists;
Guid contactID;
Entity contact = new Entity(“contact”);
protected void Page_PreInit(object sender, EventArgs e)
{
try
{
//make sure account is passed through correctly
accountID = new Guid(Request[“AccountID”].ToString());

using (OrganizationService _service = new OrganizationService(connection))
{
account = _service.Retrieve(“account”, accountID, new ColumnSet(new string[] { “name” }));
}
}

catch
{
throw;
}

try
{
contactID = new Guid(Request[“ContactID”].ToString());
contactExists = true;
}

catch (NullReferenceException)
{
contactExists = false;
}

}

Next, in the Page_Init page method, we add a condition to populate the existing controls if data already exists:

protected void Page_Init(object sender, EventArgs e)
{
try
{
if (contactExists)
{
//YOU SHOULD CHECK FOR BLANKS VVV IF YOU ARE DOING THIS IN A PROD ENVIRONMENT

//show customer name on header
ContactHeader.InnerText = contact[“fullname”].ToString();
FirstNameTextbox.Text = contact[“firstname”].ToString();
LastNameTextbox.Text = contact[“lastname”].ToString();
PhoneNumberTextbox.Text = contact[“telephone1”].ToString();

}

else
{
//show customer name on header
ContactHeader.InnerText = “Add New Contact”;

}

using (OrganizationService _service = new OrganizationService(connection))
{
//method will handle setting or leaving blank
GetDropdownList(contact, ContactTypeDDL, “new_contacttype”, _service);
}

}

catch
{
throw;
}
}

Lastly, we’ll change the Save Button event to update the existing contact if it exists:

protected void SaveButton_Click(object sender, EventArgs e)
{

try
{
Entity tempContact = new Entity(“contact”);

//YOU SHOULD CHECK FOR BLANKS VVV IF YOU ARE DOING THIS IN A PROD ENVIRONMENT

tempContact[“firstname”] = FirstNameTextbox.Text;
tempContact[“lastname”] = LastNameTextbox.Text;
tempContact[“telephone1”] = PhoneNumberTextbox.Text;
tempContact[“new_contacttype”] = new OptionSetValue(Int32.Parse(ContactTypeDDL.SelectedValue));

tempContact[“parentcustomerid”] = new EntityReference(“account”, accountID);
using (OrganizationService _service = new OrganizationService(connection))
{
if (contactExists)
{
tempContact.Id = contactID;
_service.Update(tempContact);
}

else
{
_service.Create(tempContact);
}
}
}

catch
{
throw;
}

Response.Redirect(“AccountGridview.aspx?AccountID=” + accountID);
}

And voila, that’s it! Clickable GridView!

Click here to download the Visual Studio solution used in this example.

Feel free to contact FMT Consultants should you have questions or need assistance with Microsoft Dynamics CRM.

Written by:
Henry Lin, Senior Dynamics CRM Consultant
FMT Consultants