AXForum  
Вернуться   AXForum > Microsoft Dynamics CRM > Dynamics CRM: Blogs
All
Забыли пароль?
Зарегистрироваться Правила Справка Пользователи Сообщения за день Поиск Все разделы прочитаны

 
 
Опции темы Поиск в этой теме Опции просмотра
Старый 25.12.2008, 15:05   #1  
Blog bot is offline
Blog bot
Участник
 
21,068 / 721 (67) +++++++
Регистрация: 28.10.2006
mscrm4ever: CRM 4.0 Supported Multi Select (Picklist) Control
Источник: http://mscrm4ever.blogspot.com/2008/...-picklist.html
==============





As you all know Dynamics does not support multi select controls. Of course this is not entirely true since from a design perspective having the ability to create many to many associations and using ms grid is a control none the less. Having that said, Customers will find unbelievable reasons to why they need or prefer to use a multi select control and not the out of the box grid.

I was wondering what it would take to create a fully supported multi select control (in other words a listbox)
using dynamics OOB/supported capabilities.

Before I start let me say that the idea of creating such a control does not justify the amount of code needed to make this solution run inside ms supported boundaries. But then again, once you have it, it takes approximately 20 minutes (per listbox) to implement, which is fairly reasonable.

In order to prove my point and satisfy my wonderings I devised a simple scenario of a credit card listbox on the contact entity.
The idea was to be able to relate multiple credit card types to a single contact using a regular listbox.

The first thing I did is create the necessary customizations needed to support the solution.
STEP 1 - Customizations:
1. A new entity called credit card.






2. A new N:N relationship with the contact entity (e.g. gi_gi_creditcard_contact). When you create the relationship don’t change its name. The way ms represent its relationships (e.g. prefix_entity_entity) will help you to better understand this solution.





3. Added a few Credit card types to complete the list.


4. On the contact entity I created a credit card IFRAME:






5. Added a new contact attribute called creditcarditems; The reason for this attribute is that it is used as a transport that will hold the current selected options.

Note: Notice that the attribute schema name (gi_creditcard) is the same as the credit card entity schema name. This is important since the multipicklist.aspx refers to its parent contact transport attribute by using the Entity2Name witch is gi_creditcard. You may also add a query string parameter to the contact onload event that opens the multipicklist.aspx and use that instead.



6. Added the following script to the contact entity. The script hides the transport attribute and points the iframe to an external multipicklist.aspx.
The multipicklist.aspx receives the following querystring parameters:
contact objected (crmForm.ObjectId)
The name of the entity (crmForm.ObjectTypeName)
The name of the related entity (gi_creditcard)
The name of the storage (transport) attribute (gi_creditcard).
Organization Name e.g. ORG_UNIQUE_NAME


crmForm.gi_creditcard.style.display = "none";
var url = prependOrgName("/isv/controls/multipicklist.aspx");
url += "?toentity=gi_creditcard" ;
url += "&toattribute=gi_card";
url += "&fromentity=" + crmForm.ObjectTypeName;
url += "&id=" + (crmForm.ObjectId?crmForm.ObjectId:"");
url += "&orgname=" + ORG_UNIQUE_NAME;
document.all["IFRAME_CreditCards"].src = url;


STEP 2 – Build MultiPicklist.aspx Page (control)
1. Added a new asp.net web application and created a vdir under the isv folder.





2. Changed the default.aspx page to multipicklist.aspx and added the following html. The html contains a runat server listbox ,a script that binds to the parent (contact) onsave event and fills the transport attribute with selected values and a few styling rules to make the listbox occupy the entire iframe space.





.LISTBOX
{
width:100%;
height:103%;
border:0px solid;
}












3. In the code behind I added functionality which:
a. Validates the query string parameters (Page_ValidateParameters())
b. Retrieves the credit card list from CRM (Page_RetrievePicklistOptions())
c. Marks the selected options if they exist (Page_SetSelectedOptions())


using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Xml;
using System.Text;
using Microsoft.Crm.Sdk;
using Microsoft.Crm.Sdk.Query;
using Microsoft.Crm.SdkTypeProxy;

namespace Controls
{
public partial class multipicklist : System.Web.UI.Page
{
///
/// Current Organizaton Name
///
private String OrganizationName;
///
/// The Primary Entity (contact) ObjectId (crmForm.ObjectId)
///
private Guid ObjectId;
///
/// The Primary Entity (contact) Name (typename)
///
private String Entity1Name;
///
/// The Related N:N Entity (gi_creditcard) Name
/// This should also be the name of the transport attribute
/// on the Primary Entity (gi_creditcard attribute on contact entity)
///
protected String Entity2Name;
///
/// The Related Entity PrimaryField Name (gi_card)
/// This is the Picklist Option Text
///
private String Entity2TextAttribute;
///
/// The Primary Entity PrimaryKey Name (contactid)
///
private String Entity1KeyAttribute;
///
/// The Related N:N Entity PrimaryKey Name (gi_creditcardid)
///
private String Entity2KeyAttribute;
///
/// CrmService
///
private CrmService Service;

protected void Page_Load(object sender, EventArgs e)
{
Page_ValidateParameters();
Page_CreateService();
Page_RetrievePicklistOptions();
Page_SetSelectedOptions();
}
///
/// Retrieve Options From Relationship Entity Using Fetch Message
/// And Set the Listbox Selected Items
///
private void Page_SetSelectedOptions()
{
if (!ObjectId.Equals(Guid.Empty))
{
String relationShipName = String.Format("gi_{0}_{1}", Entity2Name, Entity1Name);
StringBuilder fetchXml = new StringBuilder();
fetchXml.Append("");
fetchXml.Append("");
fetchXml.Append("");
fetchXml.Append("");
fetchXml.Append("");
fetchXml.Append("");
fetchXml.Append("");
fetchXml.Append("");

String resultXml = Service.Fetch(fetchXml.ToString());
XmlDocument fetchResult = new XmlDocument();
fetchResult.LoadXml(resultXml);
XmlNodeList relatedResults = fetchResult.SelectNodes("resultset/result");

foreach (XmlElement item in relatedResults)
{
XmlElement Entity2KeyAttributeNode = (XmlElement)item.SelectSingleNode(Entity2KeyAttribute);
ListItem CurrentItem = MultiBox.Items.FindByValue(Entity2KeyAttributeNode.InnerText.ToLower());
if( CurrentItem != null ) CurrentItem.Selected = true;
}
}
}
///
/// Retrieve the listbox options from the related entity using
/// queryexpression and fill the listbox.
///
private void Page_RetrievePicklistOptions()
{
QueryExpression creditQuery = new QueryExpression(Entity2Name);
creditQuery.ColumnSet.AddColumn(Entity2TextAttribute);
creditQuery.ColumnSet.AddColumn(Entity2KeyAttribute);
creditQuery.Distinct = true;
creditQuery.Orders.Add(new OrderExpression(Entity2TextAttribute, OrderType.Ascending));

RetrieveMultipleRequest retrieveMultipleRequest = new RetrieveMultipleRequest();
retrieveMultipleRequest.Query = creditQuery;
retrieveMultipleRequest.ReturnDynamicEntities = true;
RetrieveMultipleResponse retrieveMultipleResponse =
(RetrieveMultipleResponse)Service.Execute(retrieveMultipleRequest);

MultiBox.Items.Add( "" );
foreach (DynamicEntity creditcard in retrieveMultipleResponse.BusinessEntityCollection.BusinessEntities)
{
Key key = creditcard.Properties[Entity2KeyAttribute] as Key;
ListItem item = new ListItem(
creditcard.Properties[Entity2TextAttribute] + "",
key.Value.ToString("B"));
MultiBox.Items.Add(item);
}
}
///
/// Create the crmservice
///
private void Page_CreateService()
{
CrmAuthenticationToken token = new CrmAuthenticationToken();
token.AuthenticationType = 0;
token.OrganizationName = OrganizationName;

Service = new CrmService();
Service.CrmAuthenticationTokenValue = token;
Service.PreAuthenticate = false;
Service.UnsafeAuthenticatedConnectionSharing = true;
Service.Credentials = System.Net.CredentialCache.DefaultCredentials;

Service.Url = String.Format("{0}://{1}:{2}/mscrmservices/2007/crmservice.asmx", Request.Url.Scheme, Request.Url.Host, Request.Url.Port);
}
///
/// Validate the listbox control parameters
///
private void Page_ValidateParameters()
{
Entity1Name = Request.QueryString["fromentity"];
if (Entity1Name + "" == String.Empty)
throw new Exception("Entity1 Name is Missing");

Entity2Name = Request.QueryString["toentity"];
if (Entity2Name + "" == String.Empty)
throw new Exception("Entity2 Name is Missing");

OrganizationName = Request.QueryString["orgname"];
if (OrganizationName + "" == String.Empty)
throw new Exception("Organization Name is Missing");

ObjectId = Request.QueryString["id"] + "" == String.Empty ? Guid.Empty :
new Guid(Request.QueryString["id"]);

Entity2TextAttribute = Request.QueryString["toattribute"];
if (Entity2TextAttribute + "" == String.Empty)
throw new Exception("Picklist Text Attribute is Missing");

Entity1KeyAttribute = Entity1Name + "id";
Entity2KeyAttribute = Entity2Name + "id";
}
}
}


STEP 3 – Closing the Cycle Using a Plug-in
So why do we need a Plug-in? The main reason is that from the client side perspective CRM will create the contact id (GUID) only after the record is saved! This leaves us with no other option than to associate selected options on the server using a Plug-in.

1. Added a new Class Library application



2. Signed the application by creating a Strong Name key

3. Added Plug-In code that removes existing options (Disassociates) and saves (Re-Associates) the current contact with the selected listbox (credit card) options. The plug-in receives the existing transport attribute saved options from the PreImage and disassociates these options then gets the newly selected options from the PostImage and Re-Associate them with the contact. The Plug-in needs to run on both Post Create and Post Update events. Following is the Plug-in Specifications, SolutionXml and Code













Plug-In Solution XML























Plug-In Code:

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Crm.Sdk;
using Microsoft.Crm.SdkTypeProxy;

namespace MPPlugIn
{
public class MPHandler : IPlugin
{
#region IPlugin Members

///
/// A Multipicklist context Object
///
public class MPContext
{
///
/// MS Context
///
public IPluginExecutionContext context;
///
/// The Entity 1 (Primary: contact in this case) Primary Key e.g. contactid
///
public String Entity1PrimaryAttributeName;
///
/// The Entity 2 (related: gi_creditcard in this case) Name
///
public String Entity2Name;
///
/// An Entity 1 Attribute that is used as a transport form selected options.
/// in this case i called it gi_creditcard (same and the related entity name)
/// You can give it any name you like.
///
public String Entity1PicklistOptionsAttribute;
///
/// The relationship name of contact and gi_creditcard
/// e.g. gi_gi_creditcard_contact. this is also used as the
/// PreImage and PostImage ALIAS names.
///
public String N2NRelationShipName;
///
/// Entity 1 (Primary) Name
/// Primary means the entity where the multipicklist is put.
/// Related means the entity from which the multipicklist options are taken.
///
private String _Entity1Name;
public String Entity1Name
{
get
{
return this._Entity1Name;
}
set
{
this._Entity1Name = value;
this.Entity1PrimaryAttributeName = value + "id";
}
}
///
/// Initialize a new MP context
///
///
public MPContext(IPluginExecutionContext context) {
this.context = context;
}
}
///
/// PlugIn Implementation:
/// 1. Create a new MPContext
/// 2. Fill the MPContext parameters
/// 3. Disassociate old options
/// 4. Associate new options
///
///
public void Execute(IPluginExecutionContext context)
{
MPContext mpContext = new MPContext(context);
mpContext.Entity1Name = "contact";
mpContext.Entity2Name = "gi_creditcard";
mpContext.Entity1PicklistOptionsAttribute = "gi_creditcard";
mpContext.N2NRelationShipName = "gi_gi_creditcard_contact";

DisAssociateMultiplePicklist(mpContext);
AssociateMultiplePicklist(mpContext);
}
///
/// Retrieves the existing options from the storage (transport) attribute
/// on the primary entity (contact) and call DisassociateEntitiesRequest
/// to remove the association
///
///
private void DisAssociateMultiplePicklist(MPContext context)
{
IPluginExecutionContext msContext = context.context;
/*
Validates that the relationship name (e.g. gi_gi_creditcard_contact)
is the name you gave the PreImage Alias.
*/
if (msContext.PreEntityImages.Properties.Contains(context.N2NRelationShipName) == true)
{
//Retrieve the PreImage dynamic (Primary) entity 1 (contact)
DynamicEntity Entity1 = msContext.PreEntityImages.Properties[context.N2NRelationShipName] as DynamicEntity;
/*
Validates that the image contains the primarykey (contactid) in the property bag.
the contactid represents one side of the link (Moniker).
*/
if (Entity1.Properties.Contains(context.Entity1PrimaryAttributeName) == false)
return;
/* Extract the id (contactid) */
Guid primaryEntityId = ((Key)Entity1.Properties[context.Entity1PrimaryAttributeName]).Value;
/*
If the options (transport) attribute is missing
meaning that the its value is null then exit
*/
if( Entity1.Properties.Contains( context.Entity1PicklistOptionsAttribute ) == false )
return;
/* get the existing options */
String strOptions = Entity1.Properties[context.Entity1PicklistOptionsAttribute] + "";
if (strOptions.Equals(String.Empty)) return;

/* create an option array */
String[] mpOptions = strOptions.Split(',');
/* create a crmservice */
ICrmService Service = msContext.CreateCrmService(true);

/* for each option disassociate (remove) the link */
foreach (String option in mpOptions)
{
Guid relatedEntityId = new Guid(option);
DisassociateEntitiesRequest assocRequest = new DisassociateEntitiesRequest();
assocRequest.Moniker1 = new Moniker(context.Entity1Name, primaryEntityId);
assocRequest.Moniker2 = new Moniker(context.Entity2Name, relatedEntityId);
assocRequest.RelationshipName = context.N2NRelationShipName;
Service.Execute(assocRequest);
}
}
}
///
/// Associates the new selected options with the primary entity (contact)
/// using the selected options taken from the PostImage Propery bag.
/// See DisAssociateMultiplePicklist method for code details.
///
///
private void AssociateMultiplePicklist( MPContext context )
{
IPluginExecutionContext msContext = context.context;

if (msContext.PostEntityImages.Properties.Contains(context.N2NRelationShipName) == true)
{
DynamicEntity Entity1 = msContext.PostEntityImages.Properties[context.N2NRelationShipName] as DynamicEntity;

if (Entity1.Properties.Contains(context.Entity1PrimaryAttributeName) == false)
return;

Guid primaryEntityId = ((Key)Entity1.Properties[context.Entity1PrimaryAttributeName]).Value;

if (Entity1.Properties.Contains(context.Entity1PicklistOptionsAttribute) == false)
return;

String strOptions = Entity1.Properties[context.Entity1PicklistOptionsAttribute] + "";
if (strOptions.Equals(String.Empty)) return;

String[] mpOptions = strOptions.Split(',');
ICrmService Service = msContext.CreateCrmService(true);

foreach (String option in mpOptions)
{
Guid relatedEntityId = new Guid(option);
AssociateEntitiesRequest assocRequest = new AssociateEntitiesRequest();
assocRequest.Moniker1 = new Moniker(context.Entity1Name, primaryEntityId);
assocRequest.Moniker2 = new Moniker(context.Entity2Name, relatedEntityId);
assocRequest.RelationshipName = context.N2NRelationShipName;
Service.Execute( assocRequest );
}
}
}

#endregion
}
}


In order to add another relationship multi select control you only need to repeat step 1 ( customizations ) and part of step 3 ( Plug-In Registration and duplicating the Execute Method).

Источник: http://mscrm4ever.blogspot.com/2008/...-picklist.html
__________________
Расскажите о новых и интересных блогах по Microsoft Dynamics, напишите личное сообщение администратору.
 

Похожие темы
Тема Автор Раздел Ответов Посл. сообщение
Microsoft Dynamics CRM Team Blog: CRM 4.0: Checkbox style Multi-Select Picklist Blog bot Dynamics CRM: Blogs 0 01.04.2009 05:07
Microsoft Dynamics CRM Team Blog: Building Rich-Client Dashboards for Microsoft Dynamics CRM with Windows Presentation Foundation Blog bot Dynamics CRM: Blogs 1 31.03.2009 13:24
Jim Wang: CRM 4.0: Checkbox style Multi-Select Picklist Blog bot Dynamics CRM: Blogs 0 11.02.2009 06:05
Microsoft Dynamics CRM Team Blog: List Web Part for Microsoft Dynamics CRM 4.0 Deployment Scenarios Blog bot Dynamics CRM: Blogs 0 30.01.2009 22:05
mscrm4ever: CRM 4.0 Supported Record / Page Counter for CRM views Blog bot Dynamics CRM: Blogs 0 28.12.2008 16:07
Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск
Опции просмотра

Ваши права в разделе
Вы не можете создавать новые темы
Вы не можете отвечать в темах
Вы не можете прикреплять вложения
Вы не можете редактировать свои сообщения

BB коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.
Быстрый переход

Рейтинг@Mail.ru
Часовой пояс GMT +3, время: 13:37.
Powered by vBulletin® v3.8.5. Перевод: zCarot
Контактная информация, Реклама.