The Microsoft Dynamics CRM Blog
News and views from the Microsoft Dynamics CRM Team

Tips & Tricks: Displaying Contact Images in Microsoft Dynamics CRM

Tips & Tricks: Displaying Contact Images in Microsoft Dynamics CRM

  • Comments 18

In another of our guest blog series, CRM MVP Jürgen Beck provides a developer's view demonstrating out customizable Microsoft Dynamics CRM 3.0 is. This post is provided "AS IS" with no warranties and confer no rights. You assume all risk for your use.

Wouldn’t it be nice to see some pictures of your customer when you open the contact form?

clip_image002

These pictures could be stored as jpg-attachment in the notes of your contact:

clip_image004

The solution behind this is to create a little ASP.NET-web application which should be displayed in an IFrame in your contact form:

clip_image006

The tricky thing here is to get a one-column-sized IFrame. The solution is to replace the first section by a custom section with a “Fixed Field Width” Section Layout and a “Two Columns (1:1)” Column Format.

clip_image008

Now you can create the IFrame, which have to be linked to your web application:

clip_image010

In the Layout properties you see that you have a “One column” Layout.

clip_image012

But the most important thing is to create an ASP.NET web application with Visual Studio 2008 or Visual Web Developer 2008 Express Edition. Sorry, but in this context I cannot show you the basics, but if you are a little bit experienced with ASP.NET this should be no problem for you.

Create a Project with a name like “CrmExtensions” with the following files and the references to the files “microsoft.crm.sdk.dll” and “microsoft.crm.sdktypeproxy.dll” from the Microsoft Dynamics CRM 4.0 SDK.

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="ShowImageAttachments.aspx.cs" Inherits="CrmExtensions. ShowImageAttachments" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
    <asp:Panel ID="panelPictures" runat="server">
    </asp:Panel>
    </form>
</body>
</html>

ShowImageAttachments.aspx.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using Microsoft.Crm.SdkTypeProxy;
using Microsoft.Crm.Sdk.Query;
using Microsoft.Crm.Sdk;
namespace CrmExtensions
{
    public partial class ShowImageAttachments : System.Web.UI.Page
    {
        CrmHelper myCrmHelper = new CrmHelper();
        protected void Page_Load(object sender, EventArgs e)
        {
            Guid id = Guid.Empty;
            int objecttypecode = 0;
            try
            {
                id = new Guid(Request.QueryString["id"]);
                objecttypecode = Int16.Parse(Request.QueryString["type"]);
            }
            catch 
            {
                // This can be the Guid of a contact with pictures you want to display for new contacts.
                id = new Guid("{2EE9EA21-B900-DD11-B6E8-001871E4CE73}");
                objecttypecode = 2;
            }
            ColumnSet columns = new ColumnSet(new string[] { "filesize", "filename" });
            List<DynamicEntity> annotations = myCrmHelper.GetDynamicEntities("annotation", new string[] { "objectid", "objecttypecode" }, new object[] { id, objecttypecode }, columns);
            List<Guid> annotationids = new List<Guid>();
            foreach (DynamicEntity annotation in annotations)
            {
                Guid annotationid = annotation.GetKeyValue();
                string filename = annotation.GetString("filename");
                int filesize = annotation.GetCrmNumberValue("filesize");
                // Only small jpg-files are allowed
                if (filename.ToLower().EndsWith("jpg") && filesize < 500000)
                {
                    string imageUrl = "ShowImageAttachment.aspx?id=" + annotationid.ToString();
                    // This is the download-URL to the file itself to get the original file instead of a smaller version.
                    string navigateUrl = "/ComBeck/Activities/Attachment/download.aspx?AttachmentType=5&AttachmentId=" + annotationid;
                    HyperLink hl = new HyperLink();
                    hl.ImageUrl = imageUrl;
                    hl.NavigateUrl = navigateUrl;
                    hl.Target = "_self";
                    hl.Text = filename + " (" + (filesize / 1000).ToString("#,##0") + " KB)";
                    hl.ToolTip = filename + " (" + (filesize / 1000).ToString("#,##0") + " KB)";
                    panelPictures.Controls.Add(hl);
                }
            }
        }
    }
}

ShowImageAttachment.aspx:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="ShowImageAttachment.aspx.cs" Inherits="CrmExtensions. ShowImageAttachment" %>

ShowImageAttachment.aspx.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using Microsoft.Crm.SdkTypeProxy;
using Microsoft.Crm.Sdk.Query;

using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;

namespace CrmExtensions
{
    public partial class ShowImageAttachment : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            CrmHelper myCrmHelper = new CrmHelper();
            Guid annotationid = new Guid(Request.QueryString["id"]);
            CrmService crmService = myCrmHelper.GetCrmService();
            annotation crmAnnotation = (annotation)crmService.Retrieve("annotation", annotationid, new AllColumns());
            byte[] picture = System.Convert.FromBase64String(crmAnnotation.documentbody);
            System.IO.MemoryStream ms = new System.IO.MemoryStream(picture);
            System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(ms);
            int height = 150;
            int width = height * bmp.Width / bmp.Height;
            Response.ContentType = crmAnnotation.mimetype;
            if (bmp.Height > height)
            {
                System.Drawing.Image imgPhoto = System.Drawing.Image.FromStream(ms);
                System.Drawing.Bitmap bmPhoto = new System.Drawing.Bitmap(width, height, bmp.PixelFormat);
                bmPhoto.SetResolution(bmp.HorizontalResolution, bmp.VerticalResolution);
                Graphics grPhoto = Graphics.FromImage(bmPhoto);
                grPhoto.SmoothingMode = SmoothingMode.AntiAlias;
                grPhoto.InterpolationMode = InterpolationMode.HighQualityBicubic;
                grPhoto.PixelOffsetMode = PixelOffsetMode.HighQuality;
                grPhoto.DrawImage(imgPhoto, new Rectangle(0, 0, width, height), 0, 0, bmp.Width, bmp.Height, GraphicsUnit.Pixel);
                MemoryStream mm = new MemoryStream();
                bmPhoto.Save(Response.OutputStream, System.Drawing.Imaging.ImageFormat.Jpeg);
                imgPhoto.Dispose();
                bmPhoto.Dispose();
                grPhoto.Dispose();
            }
            else
            {
                bmp.Save(Response.OutputStream, System.Drawing.Imaging.ImageFormat.Jpeg);
            }
            bmp.Dispose();
        }
    }
}

CrmHelper.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

using System.Data;
using System.Data.SqlClient;
using System.Text;

using Microsoft.Crm.SdkTypeProxy;
using Microsoft.Crm.Sdk;
using Microsoft.Crm.Sdk.Query;
using Microsoft.Crm.SdkTypeProxy.Metadata;
using System.Collections;
using Microsoft.Crm.Sdk.Metadata;
using System.IO;

namespace CrmExtensions
{
    public partial class CrmHelper
    {

        public CrmService GetCrmService()
        {
            CrmAuthenticationToken token = new CrmAuthenticationToken();
            token.AuthenticationType = 0; //Active Directory
            token.OrganizationName = "ComBeck";
            CrmService service = new CrmService();
            service.Url = "http://crm/mscrmservices/2007/crmservice.asmx";
            service.UseDefaultCredentials = true;
            service.PreAuthenticate = true;
            service.CrmAuthenticationTokenValue = token;
            return service;
        }

        public List<DynamicEntity> GetDynamicEntities(string entityName, string[] attributes, object[] values, ColumnSetBase columns)
        {
            CrmService crmService = GetCrmService();
            RetrieveMultipleRequest rmRequest = new RetrieveMultipleRequest();
            QueryBase queryBase = null;
            if (attributes == null)
            {
                QueryExpression query = new QueryExpression();
                queryBase = query;
            }
            else
            {
                QueryByAttribute query = new QueryByAttribute();
                query.Attributes = attributes;
                query.Values = values;
                queryBase = query;
            }
            queryBase.EntityName = entityName;
            if (columns is AllColumns)
            {
                queryBase.ColumnSet = columns;
            }
            else if (columns is ColumnSet)
            {
                queryBase.ColumnSet = columns;
            }

            rmRequest.Query = queryBase;
            rmRequest.ReturnDynamicEntities = true;
            RetrieveMultipleResponse rmResponse = (RetrieveMultipleResponse)crmService.Execute(rmRequest);
            List<DynamicEntity> dynamicEntities = new List<DynamicEntity>();
            foreach (BusinessEntity be in rmResponse.BusinessEntityCollection.BusinessEntities)
            {
                dynamicEntities.Add((DynamicEntity)be);
            }
            return dynamicEntities;
        }
    }
}

This ASP.NET project has to be saved under the ISV subfolder of your CRM-folder like “C:\Inetpub\wwwroot\ISV\ComBeck”. The folder “ComBeck” has now to be configured in IIS as Application.

clip_image014

If you get a “MultipleOrganizationSoapHeaderAuthenticationProvider” exception please take care to register the file “Microsoft.Crm.WebServices.dll” from folder “C:\Inetpub\wwwroot\bin” in the Global Assembly Cache (GAC). Please do an iisreset after this.

If you have questions or need support just give me a mail (Juergen.Beck@combeck.de).

Have fun with my extension,

Jürgen Beck

Here's the missing file:

DynamicEntityExtensions.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Crm.Sdk;
using Microsoft.Crm.SdkTypeProxy;
using Microsoft.Crm.Sdk.Query;
using Microsoft.Crm.Sdk.Metadata;
namespace CrmExtensions
{
   public static class DynamicEntityExtensions
   {
       public static CrmNumber GetCrmNumber(this DynamicEntity entity, string propertyName)
       {
           return entity.GetCrmNumber(propertyName, true);
       }
       public static CrmNumber GetCrmNumber(this DynamicEntity entity, string propertyName, bool nullIfEmpty)
       {
           if (entity.Properties.Contains(propertyName))
           {
               CrmNumber crmNumber = (CrmNumber)entity.Properties[propertyName];
               if (crmNumber.IsNullSpecified && crmNumber.IsNull && nullIfEmpty)
               {
                   return null;
               }
               else
               {
                   return crmNumber;
               }
           }
           else
           {
               if (nullIfEmpty)
               {
                   return null;
               }
               return new CrmNumber { IsNullSpecified = true, IsNull = true };
           }
       }
       public static int GetCrmNumberValue(this DynamicEntity entity, string propertyName)
       {
           CrmNumber crmNumber = GetCrmNumber(entity, propertyName);
           if (crmNumber == null)
           {
               return 0;
           }
           else
           {
               return crmNumber.Value;
           }
       }
       public static string GetString(this DynamicEntity entity, string propertyName)
       {
           if (entity.Properties.Contains(propertyName))
           {
               return entity.Properties[propertyName].ToString();
           }
           else
           {
               return "";
           }
       }
       public static Key GetKey(this DynamicEntity entity)
       {
           foreach (object property in entity.Properties)
           {
               if (property is KeyProperty)
               {
                   return ((KeyProperty)property).Value;
               }
           }
           return null;
       }
       public static Guid GetKeyValue(this DynamicEntity entity)
       {
           Key key = GetKey(entity);
           if (key != null)
           {
               return key.Value;
           }
           else
           {
               return Guid.Empty;
           }
       }
   }
}
  • CRM should just do this automatically.  Go out and find a pic for the contact.  With an e-mail and name I should be able to get pics from their Facebook, Linked in, or IM profile pics.  

    An I frame should be be written to fetch this on the fly.   Similar to the way XOBNI does it in Outlook.  

  • Nice!  Thanks for sharing!

  • Seems to me that all those JPGs could make for a very large database. Would not an alternative be to store the jpg image files in SharePoint?

    Or do you have ideas for storing images in a compressed smaller format?

    Cheers Anne

  • Great example Jürgen, to make life easier for .net developers and for devs who are starting out on dynamics crm you might want to consider our linq to crm provider

    for example

    CrmDataContext context = ...;

    var image = (from a in context.Annotations

       where a.ObjectId == id

       select a.DocumentBody).ToList();

    crm data types are abstracted away as well as fetchxml and queryexpressions, just use linq!

  • Wow! I'm really going to try that on my VPC :)

  • Hi Anne,

    nice to see you here!

    SharePoint stores files also in a SQL Server Database, so there is not really a difference.

    Jpeg itself is already a compressed format. If you want to make a jpeg file smaller, you can use a tool like Microsoft Office Picture Manager and make the size (height and width) smaller.

    Best regards,

    Jürgen

  • Hi Pierre,

    sounds great! Can you share appropriate code for retrieving an image file based on a name and mail account? I could extend this sample with such feature.

    Best regards,

    Jürgen

  • Very useful, not specificly for the contacts, but it makes a very interesting addition for showing the company-logo of the accounts.

    Thank you,

    Dennis

  • With all those sychnronizations between Outlook and CRM, why the hell we need to use use those workarounds.

    I have pictures of contacts within my Outlook, well sycnhronized with my phone.

    Why to upload same pictures again to CRM ?

  • I am getting an error of 402 file not found when i run .

  • Hi Dennis,

    yea you are right. This is not only for contacts. Another idea could be to use this feature for product pictures.

    Best regards,

    Jürgen

  • Hi Martin,

    Like Martin already said, this is not only for contacts. You can use it also for an account logo or product pictures.

    But when you can share code for accessing the picture from Outlook in a save way, it should be simple to show this within CRM.

    Best regards,

    Jürgen

  • Dennis said this, not Martin in my last posting.

    Best regards,

    Jürgen

  • Hi Divya,

    It seems that you use a different URL. Can you be more specific with your error message?

    Best regards,

    Jürgen

  • Great! But I got 3 errors during compiling your code.

    Error 1 'Microsoft.Crm.Sdk.DynamicEntity' does not contain a definition for 'GetKeyValue' and no extension method 'GetKeyValue' accepting a first argument of type 'Microsoft.Crm.Sdk.DynamicEntity' could be found (are you missing a using directive or an assembly reference?)

    Error 2 'Microsoft.Crm.Sdk.DynamicEntity' does not contain a definition for 'GetString' and no extension method 'GetString' accepting a first argument of type 'Microsoft.Crm.Sdk.DynamicEntity' could be found (are you missing a using directive or an assembly reference?)

    Error 3 'Microsoft.Crm.Sdk.DynamicEntity' does not contain a definition for 'GetCrmNumberValue' and no extension method 'GetCrmNumberValue' accepting a first argument of type 'Microsoft.Crm.Sdk.DynamicEntity' could be found (are you missing a using directive or an assembly reference?)

    the corresponding code:

    Guid annotationid = annotation.GetKeyValue();

               string filename = annotation.GetString("filename");

               int filesize = annotation.GetCrmNumberValue("filesize");

    the following using should be enough, right?

    using Microsoft.Crm.SdkTypeProxy;

    using Microsoft.Crm.Sdk.Query;

    using Microsoft.Crm.Sdk;

Page 1 of 2 (18 items) 12
Leave a Comment
  • Please add 6 and 6 and type the answer here:
  • Post