Experience your
30 day trial
now!
GET STARTED
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?
These pictures could be stored as jpg-attachment in the notes of your contact:
The solution behind this is to create a little ASP.NET-web application which should be displayed in an IFrame in your contact form:
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.
Now you can create the IFrame, which have to be linked to your web application:
In the Layout properties you see that you have a “One column” Layout.
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>
<%@ 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); } } } } }
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.
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:
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.
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.
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.
Dennis said this, not Martin in my last posting.
Hi Divya,
It seems that you use a different URL. Can you be more specific with your error message?
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:
the following using should be enough, right?